This commit is contained in:
@@ -55,6 +55,7 @@ func (p *SyncCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
|
|||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
pg := progressbar.New(-1)
|
pg := progressbar.New(-1)
|
||||||
|
pg.Describe("Warming up...")
|
||||||
destroyPg := func() {
|
destroyPg := func() {
|
||||||
pg.Finish()
|
pg.Finish()
|
||||||
pg.Clear()
|
pg.Clear()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Make(remoteHostname string, callback func(v bool), w fyne.Window) *CredentialDialog {
|
func Make(remoteHostname string, callback func(ok bool, username, password string), w fyne.Window) *CredentialDialog {
|
||||||
label := canvas.NewText("Connexion to "+remoteHostname, color.Black)
|
label := canvas.NewText("Connexion to "+remoteHostname, color.Black)
|
||||||
|
|
||||||
inputUsername := widget.NewEntry()
|
inputUsername := widget.NewEntry()
|
||||||
@@ -43,13 +43,11 @@ func Make(remoteHostname string, callback func(v bool), w fyne.Window) *Credenti
|
|||||||
}
|
}
|
||||||
|
|
||||||
d := &CredentialDialog{
|
d := &CredentialDialog{
|
||||||
FormDialog: dialog.NewForm("Syncing", "Connexion", "Cancel", formItems, callback, w),
|
|
||||||
inputUsername: inputUsername,
|
inputUsername: inputUsername,
|
||||||
inputPassword: inputPassword,
|
inputPassword: inputPassword,
|
||||||
}
|
}
|
||||||
|
d.FormDialog = dialog.NewForm("Syncing", "Connexion", "Cancel", formItems, func(b bool) {
|
||||||
|
callback(b, d.inputUsername.Text, d.inputPassword.Text)
|
||||||
|
}, w)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CredentialDialog) Credentials() (string, string) {
|
|
||||||
return c.inputUsername.Text, c.inputPassword.Text
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -4,10 +4,14 @@ import (
|
|||||||
"cloudsave/cmd/gui/window/about"
|
"cloudsave/cmd/gui/window/about"
|
||||||
"cloudsave/cmd/gui/window/credential"
|
"cloudsave/cmd/gui/window/credential"
|
||||||
"cloudsave/cmd/gui/window/loading"
|
"cloudsave/cmd/gui/window/loading"
|
||||||
|
|
||||||
|
syncdialog "cloudsave/cmd/gui/window/sync"
|
||||||
"cloudsave/pkg/data"
|
"cloudsave/pkg/data"
|
||||||
"cloudsave/pkg/remote"
|
"cloudsave/pkg/remote"
|
||||||
"cloudsave/pkg/remote/client"
|
"cloudsave/pkg/remote/client"
|
||||||
|
"cloudsave/pkg/repository"
|
||||||
"cloudsave/pkg/sync"
|
"cloudsave/pkg/sync"
|
||||||
|
"cloudsave/pkg/tools/iterator"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -19,6 +23,17 @@ import (
|
|||||||
"fyne.io/fyne/v2/widget"
|
"fyne.io/fyne/v2/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
ExtraLabel struct {
|
||||||
|
*widget.Label
|
||||||
|
id string
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *ExtraLabel) SetID(id string) {
|
||||||
|
e.id = id
|
||||||
|
}
|
||||||
|
|
||||||
func Make(a fyne.App, d *data.Service) fyne.Window {
|
func Make(a fyne.App, d *data.Service) fyne.Window {
|
||||||
w := a.NewWindow("CloudSave")
|
w := a.NewWindow("CloudSave")
|
||||||
w.Resize(fyne.NewSize(1000, 700))
|
w.Resize(fyne.NewSize(1000, 700))
|
||||||
@@ -38,12 +53,17 @@ func Make(a fyne.App, d *data.Service) fyne.Window {
|
|||||||
return len(games)
|
return len(games)
|
||||||
},
|
},
|
||||||
func() fyne.CanvasObject {
|
func() fyne.CanvasObject {
|
||||||
return widget.NewLabel("template")
|
return &ExtraLabel{Label: widget.NewLabel("")}
|
||||||
},
|
},
|
||||||
func(i widget.ListItemID, o fyne.CanvasObject) {
|
func(i widget.ListItemID, o fyne.CanvasObject) {
|
||||||
o.(*widget.Label).SetText(games[i].Name)
|
o.(*ExtraLabel).SetText(games[i].Name)
|
||||||
|
o.(*ExtraLabel).SetID(games[i].ID)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
list.OnSelected = func(id widget.ListItemID) {
|
||||||
|
fmt.Println(id)
|
||||||
|
}
|
||||||
|
|
||||||
toolbar := widget.NewToolbar(
|
toolbar := widget.NewToolbar(
|
||||||
widget.NewToolbarAction(theme.FolderNewIcon(), func() {
|
widget.NewToolbarAction(theme.FolderNewIcon(), func() {
|
||||||
folderSelection := dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) {
|
folderSelection := dialog.NewFolderOpen(func(lu fyne.ListableURI, err error) {
|
||||||
@@ -121,53 +141,85 @@ func Make(a fyne.App, d *data.Service) fyne.Window {
|
|||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
|
||||||
func doSync(d *data.Service, w fyne.Window) error {
|
func doSync(d *data.Service, w fyne.Window) {
|
||||||
remotes, err := remote.All()
|
rs, err := remote.All()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
d := dialog.NewError(fmt.Errorf("failed to load datastore: %w", err), w)
|
||||||
|
d.Show()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
i := 0
|
it := iterator.New(rs)
|
||||||
nextCh := make(chan struct{})
|
if it.IsEmpty() {
|
||||||
doneCh := make(chan struct{})
|
dialog.NewInformation("Sync", "no remote configured", w).Show()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dialog := credential.Make(it.Value().URL, syncing(it, d, w), w)
|
||||||
|
dialog.Show()
|
||||||
|
}
|
||||||
|
|
||||||
var cd *credential.CredentialDialog
|
func syncing(it *iterator.Iterator[remote.Remote], d *data.Service, w fyne.Window) func(bool, string, string) {
|
||||||
|
return func(b bool, username, password string) {
|
||||||
|
if b {
|
||||||
|
r := it.Value()
|
||||||
|
|
||||||
go func() {
|
cli := client.New(r.URL, username, password)
|
||||||
nextCh <- struct{}{}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
if err := cli.Ping(); err != nil {
|
||||||
select {
|
dialog.NewError(fmt.Errorf("failed to connect to the server: %w", err), w).Show()
|
||||||
case <-doneCh:
|
return
|
||||||
return nil
|
|
||||||
case <-nextCh:
|
|
||||||
{
|
|
||||||
cd = credential.Make(remotes[i].URL, func(v bool) {
|
|
||||||
if !v {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
username, password := cd.Credentials()
|
|
||||||
cli := client.New(remotes[i].URL, username, password)
|
|
||||||
|
|
||||||
sync.NewSyncer(cli, d)
|
|
||||||
|
|
||||||
fmt.Println(i)
|
|
||||||
|
|
||||||
if i < len(remotes) {
|
|
||||||
go func() {
|
|
||||||
nextCh <- struct{}{}
|
|
||||||
}()
|
|
||||||
} else {
|
|
||||||
go func() {
|
|
||||||
doneCh <- struct{}{}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
}, w)
|
|
||||||
cd.Show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
syncDialog := syncdialog.Make(0, w)
|
||||||
|
syncDialog.Show()
|
||||||
|
|
||||||
|
s := sync.NewSyncer(cli, d)
|
||||||
|
|
||||||
|
var errg error
|
||||||
|
s.SetStateCallback(func(s sync.State, g repository.Metadata) {
|
||||||
|
switch s {
|
||||||
|
case sync.FetchingMetdata:
|
||||||
|
fyne.Do(func() {
|
||||||
|
syncDialog.UpdateLabel(fmt.Sprintf("%s: fetching metadata from repository", g.Name))
|
||||||
|
})
|
||||||
|
case sync.Pushing:
|
||||||
|
fyne.Do(func() {
|
||||||
|
syncDialog.UpdateLabel(fmt.Sprintf("%s: pushing data to the server", g.Name))
|
||||||
|
})
|
||||||
|
case sync.Pulling:
|
||||||
|
fyne.Do(func() {
|
||||||
|
syncDialog.UpdateLabel(fmt.Sprintf("%s: pull data from the server", g.Name))
|
||||||
|
})
|
||||||
|
case sync.UpToDate:
|
||||||
|
fyne.Do(func() {
|
||||||
|
syncDialog.UpdateLabel(fmt.Sprintf("%s: already up-to-date", g.Name))
|
||||||
|
})
|
||||||
|
case sync.Pushed:
|
||||||
|
fyne.Do(func() {
|
||||||
|
syncDialog.UpdateLabel(fmt.Sprintf("%s: pushed", g.Name))
|
||||||
|
})
|
||||||
|
case sync.Pulled:
|
||||||
|
fyne.Do(func() {
|
||||||
|
syncDialog.UpdateLabel(fmt.Sprintf("%s: pulled", g.Name))
|
||||||
|
})
|
||||||
|
case sync.Done:
|
||||||
|
fyne.Do(func() {
|
||||||
|
syncDialog.Dismiss()
|
||||||
|
if errg == nil {
|
||||||
|
dialog.NewInformation("Sync", "The sync ended sucessfully!", w).Show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
s.SetErrorCallback(func(err error, g repository.Metadata) {
|
||||||
|
errg = err
|
||||||
|
fyne.Do(func() {
|
||||||
|
dialog.NewError(err, w).Show()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
go s.Sync()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ const (
|
|||||||
Pushed
|
Pushed
|
||||||
Pulled
|
Pulled
|
||||||
UpToDate
|
UpToDate
|
||||||
|
Done
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -81,7 +82,7 @@ func (s *Syncer) Sync() {
|
|||||||
for _, g := range games {
|
for _, g := range games {
|
||||||
r, err := remote.One(g.ID)
|
r, err := remote.One(g.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.errorCallback(fmt.Errorf("%w: %s", ErrDatastore, err), g)
|
continue
|
||||||
}
|
}
|
||||||
if r.URL != s.cli.BaseURL() {
|
if r.URL != s.cli.BaseURL() {
|
||||||
continue
|
continue
|
||||||
@@ -90,6 +91,7 @@ func (s *Syncer) Sync() {
|
|||||||
s.errorCallback(err, g)
|
s.errorCallback(err, g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.stateCallback(Done, repository.Metadata{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Syncer) sync(g repository.Metadata) error {
|
func (s *Syncer) sync(g repository.Metadata) error {
|
||||||
|
|||||||
40
pkg/tools/iterator/iterator.go
Normal file
40
pkg/tools/iterator/iterator.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package iterator
|
||||||
|
|
||||||
|
type (
|
||||||
|
Iterator[T any] struct {
|
||||||
|
index int
|
||||||
|
values []T
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func New[T any](values []T) *Iterator[T] {
|
||||||
|
return &Iterator[T]{
|
||||||
|
values: values,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator[T]) Next() bool {
|
||||||
|
if len(it.values) == it.index {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.index += 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator[T]) Value() T {
|
||||||
|
if len(it.values) == 0 {
|
||||||
|
var zero T
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(it.values) == it.index {
|
||||||
|
var zero T
|
||||||
|
return zero
|
||||||
|
}
|
||||||
|
|
||||||
|
return it.values[it.index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *Iterator[T]) IsEmpty() bool {
|
||||||
|
return len(it.values) == 0
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user