Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Working Hours selection to enable sleep during non-working hours #15

Merged
merged 1 commit into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,37 @@ import (

const (
gitRepo string = "https://github.com/sonjek/mouse-stay-up"

workingHours00_00 = "00:00-00:00"
workingHours08_18 = "08:00-18:00"
workingHours09_19 = "09:00-19:00"
workingHours10_19 = "10:00-19:00"
workingHours10_20 = "10:00-20:00"
)

var workingHours = []string{
workingHours08_18,
workingHours09_19,
workingHours10_19,
workingHours10_20,
workingHours00_00,
}

type Config struct {
Enabled bool
GitRepo string
SleepInterval time.Duration
Enabled bool
GitRepo string
SleepInterval time.Duration
WorkingHoursInterval string
WorkingHours []string
}

func NewConfig() *Config {
return &Config{
Enabled: true,
GitRepo: gitRepo,
SleepInterval: -1,
Enabled: true,
GitRepo: gitRepo,
SleepInterval: -1,
WorkingHoursInterval: workingHours10_19,
WorkingHours: workingHours,
}
}

Expand All @@ -29,3 +47,7 @@ func (c *Config) SetSleepInterval(interval time.Duration) {
func (c *Config) SetSleepIntervalSec(sec int) {
c.SetSleepInterval(time.Duration(sec) * time.Second)
}

func (c *Config) SetWorkingHoursInterval(interval string) {
c.WorkingHoursInterval = interval
}
9 changes: 8 additions & 1 deletion internal/mouse/mouse.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"time"

"github.com/go-vgo/robotgo"

"github.com/sonjek/mouse-stay-up/internal/config"
"github.com/sonjek/mouse-stay-up/internal/utils"
)

type Controller struct {
Expand All @@ -25,6 +25,13 @@ func (c *Controller) MoveMouse() {
// Sleep before the check
c.sleep()

// Check if the current time is within working hours.
// If not, there is no reason to move the cursor.
isWorkingHours := utils.IsInWorkingHours(c.config.WorkingHoursInterval)
if !isWorkingHours {
continue
}

// Get current mouse position
curX, curY := robotgo.Location()

Expand Down
61 changes: 55 additions & 6 deletions internal/tray/tray.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ func loadIcon() ([]byte, error) {
}

type Tray struct {
mouseController *mouse.Controller
config *config.Config
intervalItems map[int]*systray.MenuItem
mouseController *mouse.Controller
config *config.Config
intervalItems map[int]*systray.MenuItem
workingHoursMenuItems map[string]*systray.MenuItem
}

func NewTray(mouseController *mouse.Controller, config *config.Config) *Tray {
return &Tray{
mouseController: mouseController,
config: config,
intervalItems: make(map[int]*systray.MenuItem),
mouseController: mouseController,
config: config,
intervalItems: make(map[int]*systray.MenuItem),
workingHoursMenuItems: make(map[string]*systray.MenuItem),
}
}

Expand All @@ -64,6 +66,7 @@ func (t *Tray) onReady() {
mEnable := systray.AddMenuItem("Enable", "Enable mouse movement")
mDisable := systray.AddMenuItem("Disable", "Disable mouse movement")
mInterval := systray.AddMenuItem("Check Interval", "Set mouse movement interval")
mWorkingHours := systray.AddMenuItem("Working hours", "Select a range of working hours")
systray.AddSeparator()
mAbout := systray.AddMenuItem("About", "Open GitHub repo")
mQuit := systray.AddMenuItem("Quit", "Quit the application")
Expand All @@ -86,6 +89,16 @@ func (t *Tray) onReady() {
// Create a channel to listen for interval item clicks
intervalClicks := t.createIntervalClicksChannel()

// Add interval selection submenu items
for _, hours := range t.config.WorkingHours {
t.addWorkingHoursItems(mWorkingHours, hours)
}

// Set a marker for the default working hours interval
t.workingHoursMenuItems[t.config.WorkingHoursInterval].Check()

workingHoursIntervalClicks := t.createWorkingHoursIntervalClicksChannel()

go func() {
for {
select {
Expand All @@ -94,16 +107,22 @@ func (t *Tray) onReady() {
mEnable.Hide()
mDisable.Show()
mInterval.Enable()
mWorkingHours.Enable()
go t.mouseController.MoveMouse()
case <-mDisable.ClickedCh:
t.config.Enabled = false
mDisable.Hide()
mEnable.Show()
mInterval.Disable()
mWorkingHours.Disable()
case interval := <-intervalClicks:
// When an interval item is clicked, update the sleep interval and checkmarks
t.config.SetSleepIntervalSec(interval)
t.updateIntervalChecks(interval)
case workingHoursInterval := <-workingHoursIntervalClicks:
// When an hours interval item is clicked, update the workingHoursInterval interval and checkmarks
t.config.SetWorkingHoursInterval(workingHoursInterval)
t.updateNightModeIntervalChecks(t.config.WorkingHoursInterval)
case <-mAbout.ClickedCh:
utils.OpenWebPage(t.config.GitRepo)
case <-mQuit.ClickedCh:
Expand Down Expand Up @@ -149,5 +168,35 @@ func (t *Tray) updateIntervalChecks(selectedInterval int) {
}
}

// Adds a submenu item for selecting a working hours interval
func (t *Tray) addWorkingHoursItems(parent *systray.MenuItem, interval string) {
t.workingHoursMenuItems[interval] = parent.AddSubMenuItem(interval, interval)
}

// Creates and returns a channel that listens to all working hours interval item clicks
func (t *Tray) createWorkingHoursIntervalClicksChannel() <-chan string {
clicks := make(chan string)
for interval, item := range t.workingHoursMenuItems {
go func(interval string, item *systray.MenuItem) {
for {
<-item.ClickedCh
clicks <- interval
}
}(interval, item)
}
return clicks
}

// Updates the checkmarks for interval selection
func (t *Tray) updateNightModeIntervalChecks(selectedInterval string) {
for interval, item := range t.workingHoursMenuItems {
if interval == selectedInterval {
item.Check()
} else {
item.Uncheck()
}
}
}

func (t *Tray) onExit() {
}
52 changes: 52 additions & 0 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
package utils

import (
"fmt"
"math/rand/v2"
"os/exec"
"runtime"
"strings"
"time"
)

// layout is the time format layout
const timeLayout = "15:04"

func OpenWebPage(url string) {
switch runtime.GOOS {
case "darwin":
Expand All @@ -15,3 +22,48 @@ func OpenWebPage(url string) {
panic("unsupported platform")
}
}

func IsInWorkingHours(timeWindow string) bool {
parts := strings.Split(timeWindow, "-")

// If the start and end times are equal, it means the interval spans the entire day
if parts[0] == parts[1] {
return true
}

startTimeBefore := MakeRandomTime(parts[0])
endTimeAfter := MakeRandomTime(parts[1])
currentTime := time.Now()

// Check if the current time is within the dynamic range
if currentTime.After(startTimeBefore) && currentTime.Before(endTimeAfter) {
return true
}
return false
}

func MakeRandomTime(inputTime string) time.Time {
// Parse input time like 19:00 to date time
parsedTime, err := time.Parse(timeLayout, inputTime)
if err != nil {
fmt.Println("Error parsing time:", err)
return time.Time{}
}

// Generate a random interval between 3 to 14 minutes
randomInterval := time.Duration(rand.N(12)+3) * time.Minute

// Randomly decide whether to add or subtract the interval
if rand.N(2) == 0 {
parsedTime = parsedTime.Add(randomInterval)
} else {
parsedTime = parsedTime.Add(-randomInterval)
}

// Get the current date
now := time.Now()

// Combine the current date with the parsed time
return time.Date(now.Year(), now.Month(), now.Day(),
parsedTime.Hour(), parsedTime.Minute(), parsedTime.Second(), 0, now.Location())
}