Some checks failed
Release / build (push) Failing after 19s
Full Syncthing tray wrapper with: - System tray with 5 icon states (idle/syncing/paused/error/disconnected) - Syncthing REST API client with auto-discovered API key - Long-polling event listener for real-time status - Transfer rate monitoring, folder tracking, recent files, conflict counting - Full context menu with folders, recent files, settings toggles - Embedded admin panel binary (webview, requires CGO) - OS notifications via beeep (per-event configurable) - Syncthing process management with auto-restart - Cross-platform installer with autostart - CI pipeline for Linux (.deb + .tar.gz) and Windows (.zip) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
84 lines
1.5 KiB
Go
84 lines
1.5 KiB
Go
package syncthing
|
|
|
|
import (
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// EventHandler is called for each batch of new events.
|
|
type EventHandler func(events []Event)
|
|
|
|
// EventListener long-polls the Syncthing event API.
|
|
type EventListener struct {
|
|
client *Client
|
|
handler EventHandler
|
|
sinceID int
|
|
stopCh chan struct{}
|
|
wg sync.WaitGroup
|
|
}
|
|
|
|
// NewEventListener creates a new event listener.
|
|
func NewEventListener(client *Client, sinceID int, handler EventHandler) *EventListener {
|
|
return &EventListener{
|
|
client: client,
|
|
handler: handler,
|
|
sinceID: sinceID,
|
|
stopCh: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// Start begins long-polling in a goroutine.
|
|
func (el *EventListener) Start() {
|
|
el.wg.Add(1)
|
|
go el.loop()
|
|
}
|
|
|
|
// Stop stops the event listener and waits for it to finish.
|
|
func (el *EventListener) Stop() {
|
|
close(el.stopCh)
|
|
el.wg.Wait()
|
|
}
|
|
|
|
// LastEventID returns the last processed event ID.
|
|
func (el *EventListener) LastEventID() int {
|
|
return el.sinceID
|
|
}
|
|
|
|
func (el *EventListener) loop() {
|
|
defer el.wg.Done()
|
|
|
|
backoff := time.Second
|
|
maxBackoff := 30 * time.Second
|
|
|
|
for {
|
|
select {
|
|
case <-el.stopCh:
|
|
return
|
|
default:
|
|
}
|
|
|
|
events, err := el.client.Events(el.sinceID, 30)
|
|
if err != nil {
|
|
log.Printf("event poll error: %v", err)
|
|
select {
|
|
case <-el.stopCh:
|
|
return
|
|
case <-time.After(backoff):
|
|
}
|
|
backoff *= 2
|
|
if backoff > maxBackoff {
|
|
backoff = maxBackoff
|
|
}
|
|
continue
|
|
}
|
|
|
|
backoff = time.Second
|
|
|
|
if len(events) > 0 {
|
|
el.sinceID = events[len(events)-1].ID
|
|
el.handler(events)
|
|
}
|
|
}
|
|
}
|