diff --git a/index.html b/index.html index 248ca61..58fa15c 100644 --- a/index.html +++ b/index.html @@ -23,14 +23,12 @@

Pomodoro Timer

Connecting to server...
- + - - + diff --git a/internal/broadcast/broadcast.go b/internal/broadcast/broadcast.go index 229ae67..deb3010 100644 --- a/internal/broadcast/broadcast.go +++ b/internal/broadcast/broadcast.go @@ -8,7 +8,7 @@ import ( ) // BroadcastMessage sends a message to all connected WebSocket clients. -func BroadcastMessage(clients map[*websocket.Conn]*models.Client, message models.BroadcastMessage) { +func BroadcastMessage(clients map[*websocket.Conn]bool, message models.BroadcastMessage) { // Marshal the message into JSON format jsonMessage, err := json.Marshal(message) if err != nil { @@ -17,11 +17,12 @@ func BroadcastMessage(clients map[*websocket.Conn]*models.Client, message models } // Iterate over all connected clients and broadcast the message - for _, client := range clients { - err := client.SendMessage(websocket.TextMessage, jsonMessage) + for client := range clients { + err := client.WriteMessage(websocket.TextMessage, jsonMessage) if err != nil { log.Printf("Error broadcasting to client: %v", err) - // The client is responsible for closing itself on error + client.Close() + delete(clients, client) // Remove the client if an error occurs } } } diff --git a/internal/pomodoro/pomodoro.go b/internal/pomodoro/pomodoro.go index c8a3b03..65705fe 100644 --- a/internal/pomodoro/pomodoro.go +++ b/internal/pomodoro/pomodoro.go @@ -1,8 +1,6 @@ package pomodoro import ( - "git.smsvc.net/pomodoro/GoTomato/internal/broadcast" - "git.smsvc.net/pomodoro/GoTomato/pkg/models" "github.com/gorilla/websocket" "sync" ) @@ -15,20 +13,12 @@ const ( ) var pomodoroRunning bool -var pomodoroPaused bool - -var pomodoroResetChannel = make(chan bool, 1) -var pomodoroPauseChannel = make(chan bool, 1) -var pomodoroResumeChannel = make(chan bool, 1) - var mu sync.Mutex // to synchronize access to shared state // RunPomodoroTimer iterates the Pomodoro work/break sessions. -func RunPomodoroTimer(clients map[*websocket.Conn]*models.Client) { +func RunPomodoroTimer(clients map[*websocket.Conn]bool) { mu.Lock() pomodoroRunning = true - pomodoroPaused = false - mu.Unlock() for session := 1; session <= sessions; session++ { if !startTimer(clients, workDuration, "Work", session) { @@ -45,53 +35,11 @@ func RunPomodoroTimer(clients map[*websocket.Conn]*models.Client) { } } - mu.Lock() pomodoroRunning = false mu.Unlock() } -// ResetPomodoro resets the running Pomodoro timer. -func ResetPomodoro(clients map[*websocket.Conn]*models.Client) { - mu.Lock() - pomodoroRunning = false // Reset the running state - pomodoroPaused = false // Reset the paused state - mu.Unlock() - - // Broadcast the reset message to all clients - broadcast.BroadcastMessage(clients, models.BroadcastMessage{ - Mode: "none", - Session: 0, - MaxSession: 0, - TimeLeft: 0, - }) - - // Send a reset signal to stop any running timers - pomodoroResetChannel <- true -} - -func PausePomodoro() { - mu.Lock() - pomodoroPaused = true - mu.Unlock() - - pomodoroPauseChannel <- true -} - -func ResumePomodoro() { - mu.Lock() - pomodoroPaused = false - mu.Unlock() - pomodoroResumeChannel <- true -} - +// IsPomodoroRunning returns the status of the timer. func IsPomodoroRunning() bool { - mu.Lock() - defer mu.Unlock() // Ensures that the mutex is unlocked after the function is done return pomodoroRunning } - -func IsPomodoroPaused() bool { - mu.Lock() - defer mu.Unlock() // Ensures that the mutex is unlocked after the function is done - return pomodoroPaused -} diff --git a/internal/pomodoro/timer.go b/internal/pomodoro/timer.go index 96595b3..c7d931b 100644 --- a/internal/pomodoro/timer.go +++ b/internal/pomodoro/timer.go @@ -7,28 +7,24 @@ import ( "time" ) +var timerStopChannel = make(chan bool, 1) + // startTimer runs the countdown and broadcasts every second. -func startTimer(clients map[*websocket.Conn]*models.Client, remainingSeconds int, mode string, session int) bool { +func startTimer(clients map[*websocket.Conn]bool, remainingSeconds int, mode string, session int) bool { for remainingSeconds > 0 { select { - case <-pomodoroResetChannel: - return false - case <-pomodoroPauseChannel: - // Nothing to set here, just waiting for the signal - case <-pomodoroResumeChannel: - // Nothing to set here, just waiting for the signal + case <-timerStopChannel: + return false // Stop the timer if a stop command is received default: // Broadcast the current state to all clients - if !IsPomodoroPaused() { - broadcast.BroadcastMessage(clients, models.BroadcastMessage{ - Mode: mode, - Session: session, - MaxSession: sessions, - TimeLeft: remainingSeconds, - }) - time.Sleep(time.Second) - remainingSeconds-- - } + broadcast.BroadcastMessage(clients, models.BroadcastMessage{ + Mode: mode, + Session: session, + MaxSession: sessions, + TimeLeft: remainingSeconds, + }) + time.Sleep(time.Second) + remainingSeconds-- } } @@ -42,3 +38,8 @@ func startTimer(clients map[*websocket.Conn]*models.Client, remainingSeconds int return true } + +// StopTimer sends a signal to stop the running Pomodoro timer. +func StopTimer() { + timerStopChannel <- true +} diff --git a/internal/websocket/client_commands.go b/internal/websocket/client_commands.go index 91ee750..41855bf 100644 --- a/internal/websocket/client_commands.go +++ b/internal/websocket/client_commands.go @@ -6,22 +6,10 @@ import ( "git.smsvc.net/pomodoro/GoTomato/pkg/models" "github.com/gorilla/websocket" "log" - "sync" ) -// Clients is a map of connected WebSocket clients, where each client is represented by the Client struct -var Clients = make(map[*websocket.Conn]*models.Client) -var mu sync.Mutex // Mutex to protect access to the Clients map - // handleClientCommands listens for commands from WebSocket clients and dispatches to the timer. func handleClientCommands(ws *websocket.Conn) { - // Create a new Client and add it to the Clients map - mu.Lock() - Clients[ws] = &models.Client{ - Conn: ws, - } - mu.Unlock() - for { _, message, err := ws.ReadMessage() if err != nil { @@ -46,17 +34,8 @@ func handleClientCommands(ws *websocket.Conn) { } case "stop": if pomodoro.IsPomodoroRunning() { - pomodoro.ResetPomodoro(Clients) // Reset Pomodoro - } - case "pause": - if pomodoro.IsPomodoroRunning() && !pomodoro.IsPomodoroPaused() { - pomodoro.PausePomodoro() // Pause the timer - } - case "resume": - if pomodoro.IsPomodoroRunning() && pomodoro.IsPomodoroPaused() { - pomodoro.ResumePomodoro() // Resume the timer + pomodoro.StopTimer() // Stop the timer in the Pomodoro package } } - } } diff --git a/internal/websocket/handle_connections.go b/internal/websocket/handle_connections.go index 0b8f63e..a7d6cfc 100644 --- a/internal/websocket/handle_connections.go +++ b/internal/websocket/handle_connections.go @@ -1,12 +1,14 @@ package websocket import ( - "git.smsvc.net/pomodoro/GoTomato/pkg/models" "github.com/gorilla/websocket" "log" "net/http" ) +// Map to track connected clients +var Clients = make(map[*websocket.Conn]bool) + // Upgrader to upgrade HTTP requests to WebSocket connections var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, @@ -23,9 +25,7 @@ func HandleConnections(w http.ResponseWriter, r *http.Request) { defer ws.Close() // Register the new client - Clients[ws] = &models.Client{ - Conn: ws, // Store the WebSocket connection - } + Clients[ws] = true // Listen for commands from the connected client handleClientCommands(ws) diff --git a/pkg/models/client.go b/pkg/models/client.go deleted file mode 100644 index 8d5c3e9..0000000 --- a/pkg/models/client.go +++ /dev/null @@ -1,30 +0,0 @@ -package models - -import ( - "github.com/gorilla/websocket" - "log" - "sync" -) - -// ClientCommand represents a command from the client (start/stop). -type ClientCommand struct { - Command string `json:"command"` -} - -type Client struct { - Conn *websocket.Conn - Mutex sync.Mutex -} - -// It automatically locks and unlocks the 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.Printf("Error writing to WebSocket: %v", err) - c.Conn.Close() // Close the connection on error - } - return err -} diff --git a/pkg/models/broadcast.go b/pkg/models/types.go similarity index 75% rename from pkg/models/broadcast.go rename to pkg/models/types.go index 78463b9..ea04e21 100644 --- a/pkg/models/broadcast.go +++ b/pkg/models/types.go @@ -7,3 +7,8 @@ type BroadcastMessage struct { MaxSession int `json:"max_session"` // Total number of sessions TimeLeft int `json:"time_left"` // Remaining time in seconds } + +// ClientCommand represents a command from the client (start/stop). +type ClientCommand struct { + Command string `json:"command"` +}