From 86eb71fbd8f7bb758a75f241c4d564dea212c802 Mon Sep 17 00:00:00 2001 From: Dmitrii Pirozhkov <7746216+nullst@users.noreply.github.com> Date: Sun, 9 Jul 2023 23:04:02 +0300 Subject: [PATCH] Force writing preferences to file when application stops --- app/app.go | 5 +++++ app/preferences.go | 17 ++++++++++++++++- internal/app/lifecycle.go | 15 ++++++++++++++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/app.go b/app/app.go index 6031334a8f..677b06028c 100644 --- a/app/app.go +++ b/app/app.go @@ -148,6 +148,11 @@ func newAppWithDriver(d fyne.Driver, id string) fyne.App { fyne.SetCurrentApp(newApp) newApp.prefs = newApp.newDefaultPreferences() + newApp.lifecycle.(*app.Lifecycle).SetAfterStopped(func() { + if prefs, ok := newApp.prefs.(*preferences); ok { + prefs.forceImmediateSave() + } + }) newApp.settings = loadSettings() store := &store{a: newApp} store.Docs = makeStoreDocs(id, newApp.prefs, store) diff --git a/app/preferences.go b/app/preferences.go index 35006c39e1..6aa4cbe874 100644 --- a/app/preferences.go +++ b/app/preferences.go @@ -19,12 +19,25 @@ type preferences struct { savedRecently bool changedDuringSaving bool - app *fyneApp + app *fyneApp + needsSaveBeforeExit bool } // Declare conformity with Preferences interface var _ fyne.Preferences = (*preferences)(nil) +// forceImmediateSave writes preferences to file immediately, ignoring the debouncing +// logic in the change listener. Does nothing if preferences are not backed with a file. +func (p *preferences) forceImmediateSave() { + if !p.needsSaveBeforeExit { + return + } + err := p.save() + if err != nil { + fyne.LogError("Failed on force saving preferences", err) + } +} + func (p *preferences) resetSavedRecently() { go func() { time.Sleep(time.Millisecond * 100) // writes are not always atomic. 10ms worked, 100 is safer. @@ -49,6 +62,7 @@ func (p *preferences) saveToFile(path string) error { p.savedRecently = true p.prefLock.Unlock() defer p.resetSavedRecently() + err := os.MkdirAll(filepath.Dir(path), 0700) if err != nil { // this is not an exists error according to docs return err @@ -132,6 +146,7 @@ func newPreferences(app *fyneApp) *preferences { return p } + p.needsSaveBeforeExit = true p.AddChangeListener(func() { if p != app.prefs { return diff --git a/internal/app/lifecycle.go b/internal/app/lifecycle.go index 195be9a3f3..1e042e5c70 100644 --- a/internal/app/lifecycle.go +++ b/internal/app/lifecycle.go @@ -16,6 +16,14 @@ type Lifecycle struct { onBackground atomic.Value // func() onStarted atomic.Value // func() onStopped atomic.Value // func() + + internalAfterStoppedTrigger atomic.Value // func() +} + +// SetAfterStopped is an internal function that lets Fyne schedule a clean-up after +// the user-provided stopped hook. +func (l *Lifecycle) SetAfterStopped(f func()) { + l.internalAfterStoppedTrigger.Store(f) } // SetOnEnteredForeground hooks into the the app becoming foreground. @@ -64,10 +72,15 @@ func (l *Lifecycle) TriggerStarted() { } } -// TriggerStopped will call the stopped hook, if one is registered. +// TriggerStopped will call the stopped hook, if one is registered, +// and an internal stopped hook after that. func (l *Lifecycle) TriggerStopped() { f := l.onStopped.Load() if ff, ok := f.(func()); ok && ff != nil { ff() } + f = l.internalAfterStoppedTrigger.Load() + if ff, ok := f.(func()); ok && ff != nil { + ff() + } }