From e6ca29a7aac060d06a4018ca1246ff16e24f0b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lie=20DELHAIE?= Date: Sun, 7 Sep 2025 19:26:18 +0200 Subject: [PATCH] first commit --- .gitignore | 1 + cmd/gui/main.go | 57 +++++++++++++++ cmd/gui/window/about/about.go | 32 ++++++++ cmd/gui/window/loading/loading.go | 24 ++++++ cmd/gui/window/mainwindow/mainwindow.go | 97 +++++++++++++++++++++++++ 5 files changed, 211 insertions(+) create mode 100644 cmd/gui/main.go create mode 100644 cmd/gui/window/about/about.go create mode 100644 cmd/gui/window/loading/loading.go create mode 100644 cmd/gui/window/mainwindow/mainwindow.go diff --git a/.gitignore b/.gitignore index 5de49e4..cc9f142 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ /cli /server /web +/gui /env/ /build/ *.exe diff --git a/cmd/gui/main.go b/cmd/gui/main.go new file mode 100644 index 0000000..c9d812d --- /dev/null +++ b/cmd/gui/main.go @@ -0,0 +1,57 @@ +package main + +import ( + "cloudsave/cmd/gui/window/mainwindow" + "cloudsave/pkg/data" + "cloudsave/pkg/repository" + "fmt" + "os" + "path/filepath" + + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/dialog" +) + +func main() { + a := app.NewWithID("com.thelilfrog.CloudSave") + + roaming, err := os.UserConfigDir() + if err != nil { + panic("failed to get user config path: " + err.Error()) + } + + datastorepath := filepath.Join(roaming, "cloudsave", "data") + err = os.MkdirAll(datastorepath, 0740) + if err != nil { + d := dialog.NewError(fmt.Errorf("cannot make the datastore: %w", err), a.NewWindow("CloudSave")) + d.Show() + d.SetOnClosed(func() { + os.Exit(1) + }) + } + + repo, err := repository.NewLazyRepository(datastorepath) + if err != nil { + d := dialog.NewError(fmt.Errorf("cannot make the datastore: %w", err), a.NewWindow("CloudSave")) + d.Show() + d.SetOnClosed(func() { + os.Exit(1) + }) + } + + s := data.NewService(repo) + + w := mainwindow.Make(a, s) + + defer func() { + if r := recover(); r != nil { + d := dialog.NewError(fmt.Errorf("the application crashed: %s", r), w) + d.Show() + d.SetOnClosed(func() { + os.Exit(1) + }) + } + }() + + w.ShowAndRun() +} diff --git a/cmd/gui/window/about/about.go b/cmd/gui/window/about/about.go new file mode 100644 index 0000000..84bee8f --- /dev/null +++ b/cmd/gui/window/about/about.go @@ -0,0 +1,32 @@ +package about + +import ( + "cloudsave/pkg/constants" + "image/color" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/layout" +) + +func Make(a fyne.App) fyne.Window { + w := a.NewWindow("About CloudSave") + w.Resize(fyne.NewSize(400, 200)) + w.CenterOnScreen() + w.SetFixedSize(true) + + title := canvas.NewText("CloudSave", color.Black) + title.Alignment = fyne.TextAlignCenter + title.TextSize = 32 + + version := canvas.NewText("v"+constants.Version, color.Black) + version.Alignment = fyne.TextAlignCenter + version.TextSize = 18 + + c := container.New(layout.NewVBoxLayout(), title, version) + + centered := container.New(layout.NewHBoxLayout(), layout.NewSpacer(), c, layout.NewSpacer()) + w.SetContent(container.New(layout.NewVBoxLayout(), centered)) + return w +} diff --git a/cmd/gui/window/loading/loading.go b/cmd/gui/window/loading/loading.go new file mode 100644 index 0000000..c10ff7a --- /dev/null +++ b/cmd/gui/window/loading/loading.go @@ -0,0 +1,24 @@ +package loading + +import ( + "image/color" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/canvas" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/layout" + "fyne.io/fyne/v2/widget" +) + +func Make(msg string, w fyne.Window) *dialog.CustomDialog { + title := canvas.NewText(msg, color.Black) + title.Alignment = fyne.TextAlignCenter + + pg := widget.NewProgressBarInfinite() + + c := container.New(layout.NewVBoxLayout(), title, pg) + d := dialog.NewCustomWithoutButtons("Loading", c, w) + + return d +} diff --git a/cmd/gui/window/mainwindow/mainwindow.go b/cmd/gui/window/mainwindow/mainwindow.go new file mode 100644 index 0000000..19193fa --- /dev/null +++ b/cmd/gui/window/mainwindow/mainwindow.go @@ -0,0 +1,97 @@ +package mainwindow + +import ( + "cloudsave/cmd/gui/window/about" + "cloudsave/cmd/gui/window/loading" + "cloudsave/pkg/data" + "fmt" + "os" + "path/filepath" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +func Make(a fyne.App, d *data.Service) fyne.Window { + w := a.NewWindow("CloudSave") + w.Resize(fyne.NewSize(1000, 700)) + w.CenterOnScreen() + + games, err := d.AllGames() + if err != nil { + d := dialog.NewError(fmt.Errorf("failed to load datastore: %w", err), w) + d.Show() + d.SetOnClosed(func() { + os.Exit(1) + }) + } + + list := widget.NewList( + func() int { + return len(games) + }, + func() fyne.CanvasObject { + return widget.NewLabel("template") + }, + func(i widget.ListItemID, o fyne.CanvasObject) { + o.(*widget.Label).SetText(games[i].Name) + }) + + toolbar := widget.NewToolbar( + widget.NewToolbarAction(theme.FolderNewIcon(), func() { + folderSelection := dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) { + confirmDialog := dialog.NewConfirm("Add", "Do you want to add and scan '"+lu.Path()+"'?", func(accepted bool) { + if !accepted { + return + } + name := filepath.Base(lu.Path()) + + loadingDialog := loading.Make("Scanning directory", w) + loadingDialog.Show() + + go func() { + gameID, err := d.Add(name, lu.Path(), "") + if err != nil { + d := dialog.NewError(fmt.Errorf("failed to create metadata the directory: %w", err), w) + d.Show() + return + } + + _, err = d.Scan(gameID) + if err != nil { + d := dialog.NewError(fmt.Errorf("failed to scan the directory: %w", err), w) + d.Show() + return + } + + games, err = d.AllGames() + if err != nil { + d := dialog.NewError(fmt.Errorf("failed to load datastore: %w", err), w) + d.Show() + } + + fyne.Do(func() { + list.Refresh() + loadingDialog.Hide() + }) + }() + }, w) + confirmDialog.Show() + }, w) + folderSelection.Show() + }), + widget.NewToolbarSeparator(), + widget.NewToolbarSpacer(), + widget.NewToolbarAction(theme.HelpIcon(), func() { + aboutWindow := about.Make(a) + aboutWindow.Show() + }), + ) + + content := container.NewBorder(toolbar, nil, nil, nil, list) + w.SetContent(content) + return w +}