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

🎉 Implement webhook #4

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions Timeow/Timeow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down Expand Up @@ -241,7 +241,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.13;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
Expand Down
5 changes: 3 additions & 2 deletions active_period.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ func (a *app) addActivePeriodEntry(start, end time.Time) {
}

entry := period{
Start: start,
End: end,
Start: start,
End: end,
IsSynced: false,
}

a.activePeriods = append(a.activePeriods, entry)
Expand Down
2 changes: 2 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const idleListenerInterval = time.Second
const timeLogCleanerInterval = time.Minute * 30
const appLabel = "com.timeow.timeow-mac"
const appName = "Timeow"
const defaultWebhookAddActivePeriod = ""
const defaultMaxAllowedIdleTime time.Duration = time.Minute * 3
const defaultKeepTimeLogsFor time.Duration = time.Hour * 24
const minAllowedActiveTime time.Duration = time.Minute
Expand All @@ -17,6 +18,7 @@ const maxAllowedIdleTimeKey = "maxAllowedIdleTime"
const keepTimeLogsForKey = "keepTimeLogsFor"
const breaksKey = "breaks"
const activePeriodsKey = "activePeriods"
const webhookAddActivePeriodKey = "webhookAddActivePeriod"

const hourInMinutes = 60
const dayInMinutes = hourInMinutes * 24
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ go 1.16

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gen2brain/dlgs v0.0.0-20220603100644-40c77870fa8d
github.com/getlantern/systray v1.1.0
github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
github.com/lextoumbourou/idle v0.0.0-20150614083601-8eb78af3d865
github.com/prashantgupta24/mac-sleep-notifier v1.0.1
github.com/sqweek/dialog v0.0.0-20240226140203-065105509627 // indirect
)
579 changes: 579 additions & 0 deletions go.sum

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package main
import (
"time"

"github.com/gen2brain/dlgs"
"github.com/getlantern/systray"
"github.com/sqweek/dialog"
)

func (a *app) handleIdleItemSelected(mIdleTimes []*systray.MenuItem, index int) {
Expand Down Expand Up @@ -44,6 +46,19 @@ func (a *app) handleQuitClicked() {
systray.Quit()
}

func (a *app) handleChangeWebHookAddActivePeriodClicked() {
url, _, _ := dlgs.Entry("Enter Webhook URL", "Enter the URL of the webhook", a.webhookAddActivePeriod)
a.setWebhookAddActivePeriod(url)
}

func (a *app) handleForceSyncClicked() {
syncError, _ := a.savePeriodsToStorage(activePeriodsKey, a.activePeriods)
if syncError != nil {
dialog.Message(syncError.Error()).Title("Error").Error()
return
}
}

func (a *app) handleAboutClicked() {
openURL(aboutURL)
}
10 changes: 8 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import (
)

type app struct {
isPro bool
isPro bool
webhookAddActivePeriod string

maxAllowedIdleTime time.Duration
keepTimeLogsFor time.Duration
Expand Down Expand Up @@ -47,7 +48,8 @@ func main() {
notifierInstance := notifier.GetInstance()
defaults := *userdefaults.Defaults()
app := app{
isPro: true,
isPro: true,
webhookAddActivePeriod: defaults.String(webhookAddActivePeriodKey),

maxAllowedIdleTime: time.Minute * time.Duration(
defaults.Integer(maxAllowedIdleTimeKey),
Expand Down Expand Up @@ -82,6 +84,10 @@ func main() {
app.setKeepTimeLogsFor(int(defaultKeepTimeLogsFor.Minutes()))
}

if app.webhookAddActivePeriod == "" {
app.setWebhookAddActivePeriod(defaultWebhookAddActivePeriod)
}

breaks, err := app.readPeriodsFromStorage(breaksKey)
if err == nil {
app.breaks = breaks
Expand Down
58 changes: 53 additions & 5 deletions period.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"

"github.com/getlantern/systray"
"github.com/hako/durafmt"
)

type period struct {
Start time.Time `json:"start"`
End time.Time `json:"end"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
IsSynced bool `json:"is_synced"`
}

func (p *period) duration() time.Duration {
Expand All @@ -30,12 +34,17 @@ func (p *period) string() string {
if duration > time.Hour {
limit = 2
}
syncedString := ""
if p.IsSynced {
syncedString = " Synced"
}

return fmt.Sprintf(
"%s - %s (%s)",
"%s - %s (%s) %s",
p.Start.Format(format),
p.End.Format(format),
durafmt.Parse(duration).LimitFirstN(limit).String(),
syncedString,
)
}

Expand All @@ -46,8 +55,47 @@ func (a *app) readPeriodsFromStorage(key string) ([]period, error) {
return periods, err
}

func (a *app) savePeriodsToStorage(key string, periods []period) error {
return a.defaults.Marshal(key, periods)
func (a *app) savePeriodsToStorage(key string, periods []period) (error, error) {
// make http request POST to localhost 8000 with body is periods when key is activePeriodsKey
var syncedError error
if key == activePeriodsKey && a.webhookAddActivePeriod != "" {
syncedError = a.syncPeriods(periods)
}
return syncedError, a.defaults.Marshal(key, periods)
}

func (a *app) syncPeriods(periods []period) error {
var unsyncedPeriods []period
for _, p := range periods {
if !p.IsSynced {
unsyncedPeriods = append(unsyncedPeriods, p)

}
}
if len(unsyncedPeriods) <= 0 {
return nil
}
payload, err := json.Marshal(unsyncedPeriods)
if err != nil {
fmt.Printf("Error: %v\n", err)
return err
}
resp, err := http.Post(a.webhookAddActivePeriod, "application/json", bytes.NewBuffer(payload))
if err != nil {
fmt.Printf("Error: %v\n", err)
return err
}
if resp.StatusCode != http.StatusCreated {
err := fmt.Errorf("unexpected status code: %v", resp.Status)
fmt.Println(err)
return err
}
// set is_synced to true for all periods
for i := range periods {
periods[i].IsSynced = true
}
defer resp.Body.Close()
return nil
}

func updatePeriodMenuItems(
Expand Down
2 changes: 1 addition & 1 deletion period_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func TestString(t *testing.T) {
for _, tt := range tests {
testname := fmt.Sprintf("%v, %v", tt.start, tt.end)
t.Run(testname, func(t *testing.T) {
entry := period{tt.start, tt.end}
entry := period{tt.start, tt.end, false}
answer := entry.string()
if answer != tt.want {
t.Fatalf("got %v, want %v", answer, tt.want)
Expand Down
7 changes: 6 additions & 1 deletion systray.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ func (a *app) onSystrayReady() {
"",
a.startup.RunningAtStartup(),
)

mChangeWebHookAddActivePeriod := mPreferences.AddSubMenuItem("Enter Webhook URL", "Enter the URL of the webhook")
mForceSync := mPreferences.AddSubMenuItem("Force Sync", "Force sync data to server")
createMinutesSelectionItems := func(
menuItem *systray.MenuItem,
selectedItem time.Duration,
Expand Down Expand Up @@ -130,6 +131,10 @@ func (a *app) onSystrayReady() {
a.handleAboutClicked()
case <-mQuit.ClickedCh:
a.handleQuitClicked()
case <-mChangeWebHookAddActivePeriod.ClickedCh:
a.handleChangeWebHookAddActivePeriodClicked()
case <-mForceSync.ClickedCh:
a.handleForceSyncClicked()
}
}
}()
Expand Down
5 changes: 5 additions & 0 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ func (a *app) setKeepTimeLogsFor(minutes int) {
a.defaults.SetInteger(keepTimeLogsForKey, minutes)
}

func (a *app) setWebhookAddActivePeriod(url string) {
a.webhookAddActivePeriod = url
a.defaults.SetString(webhookAddActivePeriodKey, url)
}

// This function exists because time.Sub behaves strangely after the machine wakes up from sleep.
func calculateDuration(start, end time.Time) time.Duration {
startSeconds := start.Unix()
Expand Down