gte/cmd/android-app/runner/runner.go

196 lines
4.1 KiB
Go

package runner
import (
"encoding/json"
"fmt"
"io"
"sync"
"time"
"ewintr.nl/gte/cmd/android-app/component"
"ewintr.nl/gte/cmd/android-app/screen"
"ewintr.nl/gte/internal/task"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/storage"
)
var runnerLock = sync.Mutex{}
type Runner struct {
fyneApp fyne.App
fyneWindow fyne.Window
conf *component.Configuration
logger *component.Logger
tasks *component.Tasks
screens []screen.Screen
status string
requests chan interface{}
refresh chan bool
fileURI fyne.URI
}
func NewRunner(conf *component.Configuration, fileURI fyne.URI, logger *component.Logger) *Runner {
return &Runner{
status: "init",
conf: conf,
logger: logger,
requests: make(chan interface{}),
refresh: make(chan bool),
fileURI: fileURI,
}
}
func (r *Runner) Init() fyne.CanvasObject {
logScreen := screen.NewLog()
logTab := container.NewTabItem("log", logScreen.Content())
r.screens = append(r.screens, logScreen)
configScreen := screen.NewConfig(r.requests)
configTab := container.NewTabItem("config", configScreen.Content())
r.screens = append(r.screens, configScreen)
stored, err := load(r.fileURI)
if err != nil {
r.logger.Log(err.Error())
}
r.logger.Log(fmt.Sprintf("loaded %d tasks from file", len(stored)))
r.tasks = component.NewTasks(r.conf, stored)
taskScreen := screen.NewTasks(r.requests)
taskTab := container.NewTabItem("tasks", taskScreen.Content())
r.screens = append(r.screens, taskScreen)
tabs := container.NewAppTabs(taskTab, configTab, logTab)
return tabs
}
func (r *Runner) Run() {
go r.refresher()
go r.processRequest()
r.backgroundSync()
}
func (r *Runner) processRequest() {
for req := range r.requests {
switch v := req.(type) {
case screen.SaveConfigRequest:
r.status = "saving..."
r.refresh <- true
for k, val := range v.Fields {
r.conf.Set(k, val)
}
r.status = "ready"
r.logger.Log("config saved")
case screen.SyncTasksRequest:
r.status = "syncing..."
r.refresh <- true
countDisp, countFetch, err := r.tasks.Sync()
if err != nil {
r.logger.Log(err.Error())
}
if countDisp > 0 || countFetch > 0 {
r.logger.Log(fmt.Sprintf("task sync: dispatched: %d, fetched: %d", countDisp, countFetch))
}
r.status = "ready"
all, err := r.tasks.All()
if err != nil {
r.logger.Log(err.Error())
break
}
if err := save(r.fileURI, all); err != nil {
r.logger.Log(err.Error())
break
}
case screen.MarkTaskDoneRequest:
if err := r.tasks.MarkDone(v.ID); err != nil {
r.logger.Log(err.Error())
}
r.logger.Log(fmt.Sprintf("marked task %q done", v.ID))
default:
r.logger.Log("request unknown")
}
r.refresh <- true
}
}
func (r *Runner) refresher() {
for <-r.refresh {
tasks, err := r.tasks.Today()
if err != nil {
r.logger.Log(err.Error())
}
sTasks := []screen.Task{}
for id, action := range tasks {
sTasks = append(sTasks, screen.Task{
ID: id,
Action: action,
})
}
state := screen.State{
Status: r.status,
Tasks: sTasks,
Config: r.conf.Fields(),
Logs: r.logger.Lines(),
}
for _, s := range r.screens {
s.Refresh(state)
}
}
}
func (r *Runner) backgroundSync() {
ticker := time.NewTicker(15 * time.Second)
for {
r.requests <- screen.SyncTasksRequest{}
<-ticker.C
}
}
func load(fu fyne.URI) ([]*task.Task, error) {
fr, err := storage.Reader(fu)
if err != nil {
return []*task.Task{}, err
}
defer fr.Close()
exists, err := storage.Exists(fu)
if !exists {
return []*task.Task{}, fmt.Errorf("uri does not exist")
}
if err != nil {
return []*task.Task{}, err
}
data, err := io.ReadAll(fr)
if err != nil {
return []*task.Task{}, err
}
storedTasks := []*task.Task{}
if err := json.Unmarshal(data, &storedTasks); err != nil {
return []*task.Task{}, err
}
return storedTasks, nil
}
func save(fu fyne.URI, tasks []*task.Task) error {
fw, err := storage.Writer(fu)
if err != nil {
return err
}
data, err := json.Marshal(tasks)
if err != nil {
return err
}
if _, err := fw.Write(data); err != nil {
return err
}
defer fw.Close()
return nil
}