feat: allow clients to send pomodoro config

- allow clients to send custom configuration for pomodoro sessions
- update RunPomodoro to accept a configuration parameter
- modify startTimer to handle session count from config
- add default pomodoro configuration in client command handling

🤖
This commit is contained in:
Sebastian Mark 2024-10-20 23:09:30 +02:00
parent 9149b1a78e
commit 2ac1aecba1
6 changed files with 46 additions and 22 deletions

View file

@ -32,6 +32,23 @@ Clients communicate with the server by sending JSON-encoded commands. Each comma
| `resume` | Resumes a paused session | `{"command": "resume"}` | | `resume` | Resumes a paused session | `{"command": "resume"}` |
| `reset` | Stops and resets the current session| `{"command": "reset"}` | | `reset` | Stops and resets the current session| `{"command": "reset"}` |
#### Optional Start Parameters
The Start-Command may contain an optional Pomodoro-Config, which allows you to customize the length of the work session, short break, long break, and the number of sessions. If no configuration is provided, the server will use default values.
Example:
```json
{
command: "start",
config: {
"work": 10, // Length of the work session in seconds
"shortBreak": 5, // Length of the short break in seconds
"longBreak": 10, // Length of the long break in seconds
"sessions": 2 // Number of total sessions
}
}
```
### Server Messages ### Server Messages
The server sends JSON-encoded messages to all connected clients to update them on the current state of the Pomodoro session. The server sends JSON-encoded messages to all connected clients to update them on the current state of the Pomodoro session.

View file

@ -61,7 +61,8 @@
// Start Button Click Event // Start Button Click Event
document.getElementById("startButton").addEventListener("click", function () { document.getElementById("startButton").addEventListener("click", function () {
ws.send(JSON.stringify({command: "start"})); // ws.send(JSON.stringify({command: "start"}));
ws.send(JSON.stringify({command: "start", config: {work: 10, shortBreak: 5, longBreak: 10, sessions: 2}}));
// Hide start button and show pause/resume and stop buttons // Hide start button and show pause/resume and stop buttons
document.getElementById("startButton").style.display = "none"; document.getElementById("startButton").style.display = "none";

View file

@ -7,13 +7,6 @@ import (
"sync" "sync"
) )
var PomodoroConfig = models.GoTomatoPomodoroConfig{
Work: 15 * 60,
ShortBreak: 5 * 60,
LongBreak: 10 * 60,
Sessions: 4,
}
var pomodoroOngoing bool var pomodoroOngoing bool
var pomodoroPaused bool var pomodoroPaused bool
@ -24,22 +17,22 @@ var pomodoroResumeChannel = make(chan bool, 1)
var mu sync.Mutex // to synchronize access to shared state var mu sync.Mutex // to synchronize access to shared state
// RunPomodoro iterates the Pomodoro work/break sessions. // RunPomodoro iterates the Pomodoro work/break sessions.
func RunPomodoro(clients map[*websocket.Conn]*models.Client) { func RunPomodoro(clients map[*websocket.Conn]*models.Client, config models.GoTomatoPomodoroConfig) {
mu.Lock() mu.Lock()
pomodoroOngoing = true pomodoroOngoing = true
pomodoroPaused = false pomodoroPaused = false
mu.Unlock() mu.Unlock()
for session := 1; session <= PomodoroConfig.Sessions; session++ { for session := 1; session <= config.Sessions; session++ {
if !startTimer(clients, PomodoroConfig.Work, "Work", session) { if !startTimer(clients, config.Work, "Work", session, config.Sessions) {
break break
} }
if session == PomodoroConfig.Sessions { if session == config.Sessions {
if !startTimer(clients, PomodoroConfig.LongBreak, "LongBreak", session) { if !startTimer(clients, config.LongBreak, "LongBreak", session, config.Sessions) {
break break
} }
} else { } else {
if !startTimer(clients, PomodoroConfig.ShortBreak, "ShortBreak", session) { if !startTimer(clients, config.ShortBreak, "ShortBreak", session, config.Sessions) {
break break
} }
} }

View file

@ -8,7 +8,7 @@ import (
) )
// startTimer runs the countdown and broadcasts every second. // 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]*models.Client, remainingSeconds int, mode string, session int, sessions int) bool {
for remainingSeconds > 0 { for remainingSeconds > 0 {
select { select {
case <-pomodoroResetChannel: case <-pomodoroResetChannel:
@ -23,7 +23,7 @@ func startTimer(clients map[*websocket.Conn]*models.Client, remainingSeconds int
broadcast.BroadcastMessage(clients, models.ServerMessage{ broadcast.BroadcastMessage(clients, models.ServerMessage{
Mode: mode, Mode: mode,
Session: session, Session: session,
MaxSession: PomodoroConfig.Sessions, MaxSession: sessions,
TimeLeft: remainingSeconds, TimeLeft: remainingSeconds,
}) })
time.Sleep(time.Second) time.Sleep(time.Second)
@ -36,7 +36,7 @@ func startTimer(clients map[*websocket.Conn]*models.Client, remainingSeconds int
broadcast.BroadcastMessage(clients, models.ServerMessage{ broadcast.BroadcastMessage(clients, models.ServerMessage{
Mode: mode, Mode: mode,
Session: session, Session: session,
MaxSession: PomodoroConfig.Sessions, MaxSession: sessions,
TimeLeft: 0, TimeLeft: 0,
}) })

View file

@ -8,9 +8,19 @@ import (
"log" "log"
) )
var unsetPomodoroConfig models.GoTomatoPomodoroConfig // used to check if client passed a config json
// handleClientCommands listens for commands from WebSocket clients and dispatches to the timer. // handleClientCommands listens for commands from WebSocket clients and dispatches to the timer.
func handleClientCommands(ws *websocket.Conn) { func handleClientCommands(ws *websocket.Conn) {
for { for {
var clientCommand models.ClientCommand
var pomodoroConfig = models.GoTomatoPomodoroConfig{
Work: 25 * 60,
ShortBreak: 5 * 60,
LongBreak: 15 * 60,
Sessions: 4,
}
_, message, err := ws.ReadMessage() _, message, err := ws.ReadMessage()
if err != nil { if err != nil {
log.Printf("Client disconnected: %v", err) log.Printf("Client disconnected: %v", err)
@ -19,18 +29,20 @@ func handleClientCommands(ws *websocket.Conn) {
} }
// Handle incoming commands // Handle incoming commands
var command models.ClientCommand err = json.Unmarshal(message, &clientCommand)
err = json.Unmarshal(message, &command)
if err != nil { if err != nil {
log.Printf("Error unmarshalling command: %v", err) log.Printf("Error unmarshalling command: %v", err)
continue continue
} }
// Process the command // Process the command
switch command.Command { switch clientCommand.Command {
case "start": case "start":
if !pomodoro.IsPomodoroOngoing() { if !pomodoro.IsPomodoroOngoing() {
go pomodoro.RunPomodoro(Clients) // Start the timer with the list of clients if clientCommand.Config != unsetPomodoroConfig {
pomodoroConfig = clientCommand.Config
}
go pomodoro.RunPomodoro(Clients, pomodoroConfig) // Start the timer with the list of clients
} }
case "stop": case "stop":
if pomodoro.IsPomodoroOngoing() { if pomodoro.IsPomodoroOngoing() {

View file

@ -9,6 +9,7 @@ import (
// ClientCommand represents a command from the client (start/stop). // ClientCommand represents a command from the client (start/stop).
type ClientCommand struct { type ClientCommand struct {
Command string `json:"command"` Command string `json:"command"`
Config GoTomatoPomodoroConfig `json:"config"`
} }
type Client struct { type Client struct {