refactor: new ui step3
This commit is contained in:
parent
24430d0fae
commit
c15cdea57d
6 changed files with 172 additions and 153 deletions
34
cmd/main.go
34
cmd/main.go
|
|
@ -4,25 +4,23 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"git.patanix.de/git/kettlebell-app/internal/data"
|
|
||||||
"git.patanix.de/git/kettlebell-app/internal/services"
|
|
||||||
"git.patanix.de/git/kettlebell-app/internal/ui"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/app"
|
"fyne.io/fyne/v2/app"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
|
"git.patanix.de/git/kettlebell-app/internal/data"
|
||||||
"git.patanix.de/git/kettlebell-app/internal/ui/theme" // <-- Neuer Theme-Import
|
"git.patanix.de/git/kettlebell-app/internal/services"
|
||||||
|
"git.patanix.de/git/kettlebell-app/internal/ui"
|
||||||
|
"git.patanix.de/git/kettlebell-app/internal/ui/theme"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// 1. App initialisieren und unser neues Theme setzen
|
// 1. App initialisieren und unser neues Theme setzen
|
||||||
myApp := app.NewWithID("com.patani.kettlebell-tracker")
|
myApp := app.NewWithID("com.patani.kettlebell-tracker")
|
||||||
myApp.Settings().SetTheme(&theme.KettlebellTheme{}) // <-- Setzt unser benutzerdefiniertes Theme
|
myApp.Settings().SetTheme(&theme.KettlebellTheme{})
|
||||||
|
|
||||||
mainWindow := myApp.NewWindow("Kettlebell Tracker")
|
mainWindow := myApp.NewWindow("Kettlebell Tracker")
|
||||||
|
|
||||||
// 2. Services initialisieren (wie zuvor)
|
// 2. Services initialisieren
|
||||||
dbDir := myApp.Storage().RootURI().Path()
|
dbDir := myApp.Storage().RootURI().Path()
|
||||||
dbPath := filepath.Join(dbDir, "giant_training.db")
|
dbPath := filepath.Join(dbDir, "giant_training.db")
|
||||||
dbService, err := data.NewDatabaseService(dbPath)
|
dbService, err := data.NewDatabaseService(dbPath)
|
||||||
|
|
@ -33,15 +31,19 @@ func main() {
|
||||||
apiService := services.NewApiService(myApp.UniqueID())
|
apiService := services.NewApiService(myApp.UniqueID())
|
||||||
trainingService := services.NewTrainingService(dbService, settingsService, apiService)
|
trainingService := services.NewTrainingService(dbService, settingsService, apiService)
|
||||||
|
|
||||||
// 3. UI-Bildschirme erstellen
|
// 3. UI-Bildschirme und Aktionen erstellen
|
||||||
contentContainer := container.NewMax()
|
contentContainer := container.NewMax()
|
||||||
|
|
||||||
// Navigationsfunktion definieren, die wir an die Screens weitergeben können
|
|
||||||
var navigateTo func(string)
|
var navigateTo func(string)
|
||||||
|
|
||||||
// Die Bildschirme erstellen und dem Container hinzufügen
|
// Erstelle den Trainings-Screen und hole seine Start-Aktion
|
||||||
homeScreen := ui.MakeHomeScreen(trainingService, func() { navigateTo("training") })
|
trainingScreen, startTrainingAction := ui.MakeTrainingScreen(trainingService, settingsService, mainWindow)
|
||||||
trainingScreen := ui.MakeTrainingScreen(trainingService, settingsService, mainWindow)
|
|
||||||
|
// Erstelle den Home-Screen und übergebe ihm die Start-Aktion und die Navigationsfunktion
|
||||||
|
homeScreen := ui.MakeHomeScreen(trainingService, dbService, func() {
|
||||||
|
startTrainingAction()
|
||||||
|
navigateTo("training")
|
||||||
|
})
|
||||||
|
|
||||||
historyScreen := ui.MakeHistoryScreen(dbService, mainWindow)
|
historyScreen := ui.MakeHistoryScreen(dbService, mainWindow)
|
||||||
settingsScreen := ui.MakeSettingsScreen(settingsService, mainWindow)
|
settingsScreen := ui.MakeSettingsScreen(settingsService, mainWindow)
|
||||||
|
|
||||||
|
|
@ -58,7 +60,7 @@ func main() {
|
||||||
|
|
||||||
// 4. Benutzerdefinierte Navigationsleiste erstellen
|
// 4. Benutzerdefinierte Navigationsleiste erstellen
|
||||||
navBar, navigateFunc := ui.MakeNavBar(screens, contentContainer)
|
navBar, navigateFunc := ui.MakeNavBar(screens, contentContainer)
|
||||||
navigateTo = navigateFunc // Weisen der Navigationsfunktion die Implementierung aus der Navbar zu
|
navigateTo = navigateFunc
|
||||||
|
|
||||||
// Initial den Home-Bildschirm anzeigen
|
// Initial den Home-Bildschirm anzeigen
|
||||||
navigateTo("home")
|
navigateTo("home")
|
||||||
|
|
@ -67,7 +69,7 @@ func main() {
|
||||||
mainLayout := container.NewBorder(nil, navBar, nil, nil, contentContainer)
|
mainLayout := container.NewBorder(nil, navBar, nil, nil, contentContainer)
|
||||||
|
|
||||||
mainWindow.SetContent(mainLayout)
|
mainWindow.SetContent(mainLayout)
|
||||||
mainWindow.Resize(fyne.NewSize(360, 740)) // Mobile-freundliche Größe
|
mainWindow.Resize(fyne.NewSize(360, 740))
|
||||||
mainWindow.SetMaster()
|
mainWindow.SetMaster()
|
||||||
mainWindow.ShowAndRun()
|
mainWindow.ShowAndRun()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
_ "modernc.org/sqlite" // Importiert den SQLite-Treiber
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ... (DatabaseService struct und NewDatabaseService bleiben gleich) ...
|
||||||
type DatabaseService struct {
|
type DatabaseService struct {
|
||||||
DB *sql.DB
|
DB *sql.DB
|
||||||
}
|
}
|
||||||
|
|
@ -17,11 +18,9 @@ func NewDatabaseService(dbPath string) (*DatabaseService, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = db.Ping(); err != nil {
|
if err = db.Ping(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
createTableSQL := `
|
createTableSQL := `
|
||||||
CREATE TABLE IF NOT EXISTS training (
|
CREATE TABLE IF NOT EXISTS training (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
@ -34,23 +33,18 @@ func NewDatabaseService(dbPath string) (*DatabaseService, error) {
|
||||||
program TEXT,
|
program TEXT,
|
||||||
blockDay INTEGER
|
blockDay INTEGER
|
||||||
);`
|
);`
|
||||||
|
|
||||||
_, err = db.Exec(createTableSQL)
|
_, err = db.Exec(createTableSQL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Erstellen der Tabelle: %v", err)
|
log.Printf("Fehler beim Erstellen der Tabelle: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hier könnten wir auch komplexere Migrationen wie dein _onUpgrade handle,
|
|
||||||
// aber für den Anfang reicht das Erstellen der Tabelle.
|
|
||||||
|
|
||||||
log.Println("Datenbank erfolgreich initialisiert.")
|
log.Println("Datenbank erfolgreich initialisiert.")
|
||||||
return &DatabaseService{DB: db}, nil
|
return &DatabaseService{DB: db}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ... (SaveTraining und GetHistory bleiben gleich) ...
|
||||||
func (s *DatabaseService) SaveTraining(session *TrainingSession) error {
|
func (s *DatabaseService) SaveTraining(session *TrainingSession) error {
|
||||||
dateStr := session.Date.Format(time.RFC3339)
|
dateStr := session.Date.Format(time.RFC3339)
|
||||||
|
|
||||||
query := `
|
query := `
|
||||||
INSERT INTO training (id, date, sets, weightLeft, weightRight, repsPerSet, duration, program, blockDay)
|
INSERT INTO training (id, date, sets, weightLeft, weightRight, repsPerSet, duration, program, blockDay)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
|
@ -68,11 +62,58 @@ func (s *DatabaseService) SaveTraining(session *TrainingSession) error {
|
||||||
if session.ID != 0 {
|
if session.ID != 0 {
|
||||||
id = session.ID
|
id = session.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := s.DB.Exec(query, id, dateStr, session.Sets, session.WeightLeft, session.WeightRight, session.RepsPerSet, session.Duration, session.Program, session.BlockDay)
|
_, err := s.DB.Exec(query, id, dateStr, session.Sets, session.WeightLeft, session.WeightRight, session.RepsPerSet, session.Duration, session.Program, session.BlockDay)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DatabaseService) GetHistory() ([]TrainingSession, error) {
|
||||||
|
query := `SELECT id, date, sets, weightLeft, weightRight, repsPerSet, duration, program, blockDay FROM training ORDER BY date DESC LIMIT 20;`
|
||||||
|
rows, err := s.DB.Query(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var sessions []TrainingSession
|
||||||
|
for rows.Next() {
|
||||||
|
var sess TrainingSession
|
||||||
|
var dateStr string
|
||||||
|
err := rows.Scan(&sess.ID, &dateStr, &sess.Sets, &sess.WeightLeft, &sess.WeightRight, &sess.RepsPerSet, &sess.Duration, &sess.Program, &sess.BlockDay)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sess.Date, err = time.Parse(time.RFC3339, dateStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sessions = append(sessions, sess)
|
||||||
|
}
|
||||||
|
return sessions, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLastTraining ruft die letzte einzelne Trainingseinheit ab.
|
||||||
|
func (s *DatabaseService) GetLastTraining() (*TrainingSession, error) {
|
||||||
|
query := `SELECT id, date, sets, weightLeft, weightRight, repsPerSet, duration, program, blockDay FROM training ORDER BY date DESC LIMIT 1;`
|
||||||
|
row := s.DB.QueryRow(query)
|
||||||
|
|
||||||
|
var session TrainingSession
|
||||||
|
var dateStr string
|
||||||
|
err := row.Scan(&session.ID, &dateStr, &session.Sets, &session.WeightLeft, &session.WeightRight, &session.RepsPerSet, &session.Duration, &session.Program, &session.BlockDay)
|
||||||
|
if err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return nil, nil // Kein Fehler, nur keine Einträge
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Date, err = time.Parse(time.RFC3339, dateStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &session, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTrainingCount zählt alle Trainingseinheiten.
|
||||||
func (s *DatabaseService) GetTrainingCount() (int, error) {
|
func (s *DatabaseService) GetTrainingCount() (int, error) {
|
||||||
var count int
|
var count int
|
||||||
query := "SELECT COUNT(*) FROM training;"
|
query := "SELECT COUNT(*) FROM training;"
|
||||||
|
|
@ -85,33 +126,3 @@ func (s *DatabaseService) GetTrainingCount() (int, error) {
|
||||||
}
|
}
|
||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DatabaseService) GetHistory() ([]TrainingSession, error) {
|
|
||||||
query := `SELECT id, date, sets, weightLeft, weightRight, repsPerSet, duration, program, blockDay FROM training ORDER BY date DESC LIMIT 20;`
|
|
||||||
|
|
||||||
rows, err := s.DB.Query(query)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
var sessions []TrainingSession
|
|
||||||
for rows.Next() {
|
|
||||||
var s TrainingSession
|
|
||||||
var dateStr string
|
|
||||||
|
|
||||||
err := rows.Scan(&s.ID, &dateStr, &s.Sets, &s.WeightLeft, &s.WeightRight, &s.RepsPerSet, &s.Duration, &s.Program, &s.BlockDay)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Date, err = time.Parse(time.RFC3339, dateStr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
sessions = append(sessions, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
return sessions, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"git.patanix.de/git/kettlebell-app/internal/data"
|
|
||||||
"git.patanix.de/git/kettlebell-app/internal/ui/utils"
|
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/container"
|
"fyne.io/fyne/v2/container"
|
||||||
"fyne.io/fyne/v2/dialog"
|
"fyne.io/fyne/v2/dialog"
|
||||||
"fyne.io/fyne/v2/theme"
|
"fyne.io/fyne/v2/theme"
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
|
"git.patanix.de/git/kettlebell-app/internal/data"
|
||||||
|
"git.patanix.de/git/kettlebell-app/internal/ui/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.CanvasObject {
|
func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.CanvasObject {
|
||||||
|
|
@ -19,19 +18,14 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
|
||||||
|
|
||||||
placeholder := widget.NewLabel("Noch keine Trainingsdaten vorhanden.")
|
placeholder := widget.NewLabel("Noch keine Trainingsdaten vorhanden.")
|
||||||
list := widget.NewList(
|
list := widget.NewList(
|
||||||
func() int {
|
func() int { return len(history) },
|
||||||
return len(history)
|
|
||||||
},
|
|
||||||
func() fyne.CanvasObject {
|
func() fyne.CanvasObject {
|
||||||
return widget.NewCard("", "", container.NewVBox(
|
stats := container.NewGridWithColumns(3,
|
||||||
widget.NewLabel(""),
|
container.NewVBox(widget.NewLabel("Sätze"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
||||||
widget.NewSeparator(),
|
container.NewVBox(widget.NewLabel("Dauer"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
||||||
container.NewGridWithColumns(3,
|
container.NewVBox(widget.NewLabel("Reps/Satz"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
||||||
container.NewVBox(widget.NewLabel("Sätze"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
)
|
||||||
container.NewVBox(widget.NewLabel("Dauer"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
return widget.NewCard("", "", container.NewVBox(widget.NewLabel(""), widget.NewSeparator(), stats))
|
||||||
container.NewVBox(widget.NewLabel("Reps/Satz"), widget.NewLabelWithStyle("", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
func(i widget.ListItemID, o fyne.CanvasObject) {
|
func(i widget.ListItemID, o fyne.CanvasObject) {
|
||||||
session := history[i]
|
session := history[i]
|
||||||
|
|
@ -43,7 +37,6 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
|
||||||
statsGrid := content.Objects[2].(*fyne.Container)
|
statsGrid := content.Objects[2].(*fyne.Container)
|
||||||
|
|
||||||
programLabel.SetText(fmt.Sprintf("%s - Tag %d", session.Program, session.BlockDay))
|
programLabel.SetText(fmt.Sprintf("%s - Tag %d", session.Program, session.BlockDay))
|
||||||
|
|
||||||
statsGrid.Objects[0].(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", session.Sets))
|
statsGrid.Objects[0].(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", session.Sets))
|
||||||
statsGrid.Objects[1].(*fyne.Container).Objects[1].(*widget.Label).SetText(utils.FormatDuration(session.Duration))
|
statsGrid.Objects[1].(*fyne.Container).Objects[1].(*widget.Label).SetText(utils.FormatDuration(session.Duration))
|
||||||
statsGrid.Objects[2].(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", session.RepsPerSet))
|
statsGrid.Objects[2].(*fyne.Container).Objects[1].(*widget.Label).SetText(fmt.Sprintf("%d", session.RepsPerSet))
|
||||||
|
|
@ -56,32 +49,25 @@ func MakeHistoryScreen(db *data.DatabaseService, parent fyne.Window) fyne.Canvas
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Fehler beim Laden der Historie: %v", err)
|
log.Printf("Fehler beim Laden der Historie: %v", err)
|
||||||
dialog.ShowError(err, parent)
|
dialog.ShowError(err, parent)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if len(history) == 0 {
|
if len(history) == 0 {
|
||||||
list.Hide()
|
|
||||||
placeholder.Show()
|
placeholder.Show()
|
||||||
|
list.Hide()
|
||||||
} else {
|
} else {
|
||||||
list.Show()
|
|
||||||
placeholder.Hide()
|
placeholder.Hide()
|
||||||
|
list.Show()
|
||||||
}
|
}
|
||||||
list.Refresh()
|
list.Refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
list.OnSelected = func(id widget.ListItemID) {
|
toolbar := widget.NewToolbar(widget.NewToolbarAction(theme.ViewRefreshIcon(), refreshData))
|
||||||
list.Unselect(id)
|
content := container.NewStack(list, container.NewCenter(placeholder))
|
||||||
}
|
layout := container.NewBorder(toolbar, nil, nil, nil, content)
|
||||||
|
|
||||||
toolbar := widget.NewToolbar(
|
// Daten laden, wenn der Container sichtbar wird
|
||||||
widget.NewToolbarAction(theme.ViewRefreshIcon(), refreshData),
|
if layout.Visible() {
|
||||||
)
|
|
||||||
|
|
||||||
layout := container.NewBorder(toolbar, nil, nil, nil, container.NewStack(list, container.NewCenter(placeholder)))
|
|
||||||
|
|
||||||
wrapper := container.NewMax(layout)
|
|
||||||
if wrapper.Visible() {
|
|
||||||
refreshData()
|
refreshData()
|
||||||
}
|
}
|
||||||
// wrapper.OnVisible = refreshData
|
return layout
|
||||||
|
|
||||||
return wrapper
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,12 @@ package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"git.patanix.de/git/kettlebell-app/internal/data"
|
||||||
"git.patanix.de/git/kettlebell-app/internal/services"
|
"git.patanix.de/git/kettlebell-app/internal/services"
|
||||||
"git.patanix.de/git/kettlebell-app/internal/ui/theme"
|
"git.patanix.de/git/kettlebell-app/internal/ui/theme"
|
||||||
|
"git.patanix.de/git/kettlebell-app/internal/ui/utils"
|
||||||
|
|
||||||
"fyne.io/fyne/v2"
|
"fyne.io/fyne/v2"
|
||||||
"fyne.io/fyne/v2/canvas"
|
"fyne.io/fyne/v2/canvas"
|
||||||
|
|
@ -12,7 +15,7 @@ import (
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeHomeScreen(ts *services.TrainingService, onStart func()) fyne.CanvasObject {
|
func MakeHomeScreen(ts *services.TrainingService, db *data.DatabaseService, onStart func()) fyne.CanvasObject {
|
||||||
// Header
|
// Header
|
||||||
headerTitle := canvas.NewText("Patanix", theme.ColorSlate200)
|
headerTitle := canvas.NewText("Patanix", theme.ColorSlate200)
|
||||||
headerTitle.TextSize = 28
|
headerTitle.TextSize = 28
|
||||||
|
|
@ -25,8 +28,6 @@ func MakeHomeScreen(ts *services.TrainingService, onStart func()) fyne.CanvasObj
|
||||||
|
|
||||||
// Nächstes Training CTA
|
// Nächstes Training CTA
|
||||||
state := ts.State
|
state := ts.State
|
||||||
|
|
||||||
// Verwende ein Card-Widget für das Styling
|
|
||||||
nextTrainingCard := widget.NewCard(
|
nextTrainingCard := widget.NewCard(
|
||||||
"Nächstes Training",
|
"Nächstes Training",
|
||||||
fmt.Sprintf("%s - Tag %d", state.CurrentProgram, state.CurrentBlockDay),
|
fmt.Sprintf("%s - Tag %d", state.CurrentProgram, state.CurrentBlockDay),
|
||||||
|
|
@ -37,12 +38,30 @@ func MakeHomeScreen(ts *services.TrainingService, onStart func()) fyne.CanvasObj
|
||||||
)
|
)
|
||||||
|
|
||||||
// Letzte Leistung
|
// Letzte Leistung
|
||||||
|
setsValue := widget.NewLabelWithStyle("–", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})
|
||||||
|
durationValue := widget.NewLabelWithStyle("–", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})
|
||||||
|
weightValue := widget.NewLabelWithStyle("–", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})
|
||||||
|
|
||||||
statsCard := widget.NewCard("Letzte Leistung", "", container.NewGridWithColumns(3,
|
statsCard := widget.NewCard("Letzte Leistung", "", container.NewGridWithColumns(3,
|
||||||
container.NewVBox(widget.NewLabel("Sätze"), widget.NewLabelWithStyle("8", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
container.NewVBox(widget.NewLabel("Sätze"), setsValue),
|
||||||
container.NewVBox(widget.NewLabel("Dauer"), widget.NewLabelWithStyle("18:45", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
container.NewVBox(widget.NewLabel("Dauer"), durationValue),
|
||||||
container.NewVBox(widget.NewLabel("Gewicht"), widget.NewLabelWithStyle("16kg", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})),
|
container.NewVBox(widget.NewLabel("Gewicht"), weightValue),
|
||||||
))
|
))
|
||||||
|
|
||||||
|
// Funktion zum Laden der letzten Leistung
|
||||||
|
loadLastPerformance := func() {
|
||||||
|
lastSession, err := db.GetLastTraining()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Fehler beim Laden der letzten Session: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lastSession != nil {
|
||||||
|
setsValue.SetText(fmt.Sprintf("%d", lastSession.Sets))
|
||||||
|
durationValue.SetText(utils.FormatDuration(lastSession.Duration))
|
||||||
|
weightValue.SetText(fmt.Sprintf("%.1fkg", lastSession.WeightLeft)) // Annahme: linkes Gewicht ist repräsentativ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
layout := container.NewVBox(
|
layout := container.NewVBox(
|
||||||
header,
|
header,
|
||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
|
|
@ -50,5 +69,12 @@ func MakeHomeScreen(ts *services.TrainingService, onStart func()) fyne.CanvasObj
|
||||||
statsCard,
|
statsCard,
|
||||||
)
|
)
|
||||||
|
|
||||||
return container.NewPadded(layout)
|
paddedLayout := container.NewPadded(layout)
|
||||||
|
// Daten laden, wenn der Bildschirm sichtbar wird
|
||||||
|
if paddedLayout.Visible() {
|
||||||
|
loadLastPerformance()
|
||||||
|
}
|
||||||
|
// paddedLayout.OnVisible = loadLastPerformance
|
||||||
|
|
||||||
|
return paddedLayout
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeSettingsScreen(settingsService *services.SettingsService, parent fyne.Window) fyne.CanvasObject {
|
func MakeSettingsScreen(settingsService *services.SettingsService, parent fyne.Window) fyne.CanvasObject {
|
||||||
currentSettings := settingsService.LoadSettings()
|
var timeEntry, setsEntry, weightLeftEntry, weightRightEntry *widget.Entry
|
||||||
|
|
||||||
timeEntry := widget.NewEntry()
|
loadData := func() {
|
||||||
timeEntry.SetText(fmt.Sprintf("%d", currentSettings.TrainingTimeMinutes))
|
currentSettings := settingsService.LoadSettings()
|
||||||
|
timeEntry.SetText(fmt.Sprintf("%d", currentSettings.TrainingTimeMinutes))
|
||||||
|
setsEntry.SetText(fmt.Sprintf("%d", currentSettings.GoalSets))
|
||||||
|
weightLeftEntry.SetText(fmt.Sprintf("%.1f", currentSettings.WeightLeft))
|
||||||
|
weightRightEntry.SetText(fmt.Sprintf("%.1f", currentSettings.WeightRight))
|
||||||
|
}
|
||||||
|
|
||||||
|
timeEntry = widget.NewEntry()
|
||||||
timeEntry.Validator = func(s string) error {
|
timeEntry.Validator = func(s string) error {
|
||||||
if _, err := strconv.Atoi(s); err != nil {
|
if _, err := strconv.Atoi(s); err != nil {
|
||||||
return fmt.Errorf("muss eine Zahl sein")
|
return fmt.Errorf("muss eine Zahl sein")
|
||||||
|
|
@ -23,22 +30,18 @@ func MakeSettingsScreen(settingsService *services.SettingsService, parent fyne.W
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
setsEntry := widget.NewEntry()
|
setsEntry = widget.NewEntry()
|
||||||
setsEntry.SetText(fmt.Sprintf("%d", currentSettings.GoalSets))
|
setsEntry.Validator = timeEntry.Validator
|
||||||
setsEntry.Validator = timeEntry.Validator // Gleicher Validator
|
|
||||||
|
|
||||||
weightLeftEntry := widget.NewEntry()
|
weightLeftEntry = widget.NewEntry()
|
||||||
weightLeftEntry.SetText(fmt.Sprintf("%.1f", currentSettings.WeightLeft))
|
|
||||||
weightLeftEntry.Validator = func(s string) error {
|
weightLeftEntry.Validator = func(s string) error {
|
||||||
if _, err := strconv.ParseFloat(s, 64); err != nil {
|
if _, err := strconv.ParseFloat(s, 64); err != nil {
|
||||||
return fmt.Errorf("muss eine Zahl sein")
|
return fmt.Errorf("muss eine Zahl sein")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
weightRightEntry = widget.NewEntry()
|
||||||
weightRightEntry := widget.NewEntry()
|
weightRightEntry.Validator = weightLeftEntry.Validator
|
||||||
weightRightEntry.SetText(fmt.Sprintf("%.1f", currentSettings.WeightRight))
|
|
||||||
weightRightEntry.Validator = weightLeftEntry.Validator // Gleicher Validator
|
|
||||||
|
|
||||||
form := &widget.Form{
|
form := &widget.Form{
|
||||||
Items: []*widget.FormItem{
|
Items: []*widget.FormItem{
|
||||||
|
|
@ -58,10 +61,9 @@ func MakeSettingsScreen(settingsService *services.SettingsService, parent fyne.W
|
||||||
GoalSets: goal,
|
GoalSets: goal,
|
||||||
WeightLeft: weightL,
|
WeightLeft: weightL,
|
||||||
WeightRight: weightR,
|
WeightRight: weightR,
|
||||||
InitialProgram: currentSettings.InitialProgram, // Beibehalten
|
InitialProgram: settingsService.LoadSettings().InitialProgram,
|
||||||
}
|
}
|
||||||
settingsService.SaveSettings(newSettings)
|
settingsService.SaveSettings(newSettings)
|
||||||
|
|
||||||
fyne.CurrentApp().SendNotification(&fyne.Notification{
|
fyne.CurrentApp().SendNotification(&fyne.Notification{
|
||||||
Title: "Gespeichert",
|
Title: "Gespeichert",
|
||||||
Content: "Die Einstellungen wurden erfolgreich aktualisiert.",
|
Content: "Die Einstellungen wurden erfolgreich aktualisiert.",
|
||||||
|
|
@ -70,13 +72,11 @@ func MakeSettingsScreen(settingsService *services.SettingsService, parent fyne.W
|
||||||
}
|
}
|
||||||
|
|
||||||
title := widget.NewLabelWithStyle("Einstellungen", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
title := widget.NewLabelWithStyle("Einstellungen", fyne.TextAlignLeading, fyne.TextStyle{Bold: true})
|
||||||
title.TextStyle.Monospace = true // Workaround to force refresh on size change
|
layout := container.NewVBox(title, widget.NewSeparator(), form)
|
||||||
|
paddedLayout := container.NewPadded(layout)
|
||||||
|
if paddedLayout.Visible() {
|
||||||
|
loadData()
|
||||||
|
} // Daten laden, wenn sichtbar
|
||||||
|
|
||||||
layout := container.NewVBox(
|
return paddedLayout
|
||||||
title,
|
|
||||||
widget.NewSeparator(),
|
|
||||||
form,
|
|
||||||
)
|
|
||||||
|
|
||||||
return container.NewPadded(layout)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,24 +15,23 @@ import (
|
||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsService, parent fyne.Window) fyne.CanvasObject {
|
// MakeTrainingScreen gibt jetzt das CanvasObject und die Start-Funktion zurück
|
||||||
// UI-Elemente mit canvas.Text für die Größensteuerung
|
func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsService, parent fyne.Window) (fyne.CanvasObject, func()) {
|
||||||
timerLabel := canvas.NewText("20:00", theme.ColorSlate200)
|
timerLabel := canvas.NewText("00:00", theme.ColorSlate200)
|
||||||
timerLabel.TextSize = 60
|
timerLabel.TextSize = 60
|
||||||
timerLabel.TextStyle.Bold = true
|
timerLabel.TextStyle.Bold = true
|
||||||
timerLabel.Alignment = fyne.TextAlignCenter
|
timerLabel.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
setsLabel := canvas.NewText("0 / 8", theme.ColorSky400)
|
setsLabel := canvas.NewText("0 / 0", theme.ColorSky400)
|
||||||
setsLabel.TextSize = 48
|
setsLabel.TextSize = 48
|
||||||
setsLabel.TextStyle.Bold = true
|
setsLabel.TextStyle.Bold = true
|
||||||
setsLabel.Alignment = fyne.TextAlignCenter
|
setsLabel.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
repsLabel := canvas.NewText("5 Wiederholungen", theme.ColorSlate200)
|
repsLabel := canvas.NewText("0 Wiederholungen", theme.ColorSlate200)
|
||||||
repsLabel.TextSize = 20
|
repsLabel.TextSize = 20
|
||||||
repsLabel.Alignment = fyne.TextAlignCenter
|
repsLabel.Alignment = fyne.TextAlignCenter
|
||||||
|
|
||||||
var mainTimer *time.Ticker
|
var mainTimer *time.Ticker
|
||||||
var startButton *widget.Button // Start-Button deklarieren
|
|
||||||
|
|
||||||
updateUI := func() {
|
updateUI := func() {
|
||||||
state := ts.State
|
state := ts.State
|
||||||
|
|
@ -43,12 +42,6 @@ func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsServi
|
||||||
timerLabel.Refresh()
|
timerLabel.Refresh()
|
||||||
setsLabel.Refresh()
|
setsLabel.Refresh()
|
||||||
repsLabel.Refresh()
|
repsLabel.Refresh()
|
||||||
|
|
||||||
if state.IsTrainingRunning {
|
|
||||||
startButton.Disable()
|
|
||||||
} else {
|
|
||||||
startButton.Enable()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
finishAction := func() {
|
finishAction := func() {
|
||||||
|
|
@ -56,6 +49,9 @@ func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsServi
|
||||||
mainTimer.Stop()
|
mainTimer.Stop()
|
||||||
mainTimer = nil
|
mainTimer = nil
|
||||||
}
|
}
|
||||||
|
if !ts.State.IsTrainingRunning {
|
||||||
|
return // Nichts tun, wenn kein Training läuft
|
||||||
|
}
|
||||||
session := &data.TrainingSession{
|
session := &data.TrainingSession{
|
||||||
Date: time.Now(),
|
Date: time.Now(),
|
||||||
Sets: int64(ts.State.SetsDone),
|
Sets: int64(ts.State.SetsDone),
|
||||||
|
|
@ -70,6 +66,9 @@ func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsServi
|
||||||
}
|
}
|
||||||
|
|
||||||
startAction := func() {
|
startAction := func() {
|
||||||
|
if ts.State.IsTrainingRunning {
|
||||||
|
return // Verhindere Neustart
|
||||||
|
}
|
||||||
settings := ss.LoadSettings()
|
settings := ss.LoadSettings()
|
||||||
ts.StartTraining(settings.TrainingTimeMinutes, settings.GoalSets)
|
ts.StartTraining(settings.TrainingTimeMinutes, settings.GoalSets)
|
||||||
updateUI()
|
updateUI()
|
||||||
|
|
@ -77,48 +76,43 @@ func MakeTrainingScreen(ts *services.TrainingService, ss *services.SettingsServi
|
||||||
mainTimer = time.NewTicker(time.Second)
|
mainTimer = time.NewTicker(time.Second)
|
||||||
go func() {
|
go func() {
|
||||||
for mainTimer != nil {
|
for mainTimer != nil {
|
||||||
<-mainTimer.C
|
select {
|
||||||
if ts.State.RemainingSeconds <= 0 {
|
case <-mainTimer.C:
|
||||||
finishAction()
|
if ts.State.RemainingSeconds <= 0 {
|
||||||
return
|
finishAction()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ts.Tick()
|
||||||
|
updateUI()
|
||||||
}
|
}
|
||||||
ts.Tick()
|
|
||||||
updateUI()
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
setAction := func() {
|
setAction := func() {
|
||||||
if !ts.State.IsTrainingRunning {
|
if !ts.State.IsTrainingRunning {
|
||||||
// Starte das Training, wenn es noch nicht läuft
|
return // Kein Satz ohne laufendes Training
|
||||||
startAction()
|
|
||||||
}
|
}
|
||||||
ts.CompleteSet()
|
ts.CompleteSet()
|
||||||
updateUI()
|
updateUI()
|
||||||
}
|
}
|
||||||
|
|
||||||
startButton = widget.NewButton("Training beginnen", startAction)
|
topPart := container.NewVBox(widget.NewLabelWithStyle("Verbleibende Zeit", fyne.TextAlignCenter, fyne.TextStyle{}), timerLabel)
|
||||||
|
middlePart := container.NewVBox(widget.NewLabelWithStyle("Sätze", fyne.TextAlignCenter, fyne.TextStyle{}), setsLabel, repsLabel)
|
||||||
// Layout im "Cockpit"-Stil
|
|
||||||
topPart := container.NewVBox(
|
|
||||||
widget.NewLabelWithStyle("Verbleibende Zeit", fyne.TextAlignCenter, fyne.TextStyle{}),
|
|
||||||
timerLabel,
|
|
||||||
)
|
|
||||||
|
|
||||||
middlePart := container.NewVBox(
|
|
||||||
widget.NewLabelWithStyle("Sätze", fyne.TextAlignCenter, fyne.TextStyle{}),
|
|
||||||
setsLabel,
|
|
||||||
repsLabel,
|
|
||||||
)
|
|
||||||
|
|
||||||
finishButton := widget.NewButton("Training beenden", finishAction)
|
finishButton := widget.NewButton("Training beenden", finishAction)
|
||||||
finishButton.Importance = widget.HighImportance
|
finishButton.Importance = widget.HighImportance
|
||||||
|
|
||||||
bottomPart := container.NewVBox(
|
bottomPart := container.NewVBox(
|
||||||
startButton, // Start-Button hinzugefügt
|
|
||||||
widget.NewButton("Satz abschließen", setAction),
|
widget.NewButton("Satz abschließen", setAction),
|
||||||
finishButton,
|
finishButton,
|
||||||
)
|
)
|
||||||
|
|
||||||
return container.NewBorder(topPart, bottomPart, nil, nil, container.NewCenter(middlePart))
|
layout := container.NewBorder(topPart, bottomPart, nil, nil, container.NewCenter(middlePart))
|
||||||
|
// UI aktualisieren, wenn der Bildschirm sichtbar wird
|
||||||
|
if layout.Visible() {
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
// layout.OnVisible = updateUI
|
||||||
|
|
||||||
|
return layout, startAction
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue