From 7c08fe44282c076e2470fe19a1eacff209cfa4f2 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 9 Apr 2026 08:38:33 +0300 Subject: Fix shared text cache cleanup --- android_shared_android.go | 16 +++---- android_shared_stub.go | 2 + main.go | 72 +++++++++++++++++++++------- main_test.go | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 182 insertions(+), 24 deletions(-) diff --git a/android_shared_android.go b/android_shared_android.go index e46fab2..b29f830 100644 --- a/android_shared_android.go +++ b/android_shared_android.go @@ -7,20 +7,20 @@ import ( "path/filepath" ) -// readSharedFromCache tries to read the shared text written by the Android activity. -// The activity writes into getCacheDir()/quicklogger-shared.txt; on Go, os.TempDir() -// maps to the same location in Android (app cache directory). -func readSharedFromCache() (string, error) { +// sharedTextCachePath returns the cache file used for Android share intents. +func sharedTextCachePath() string { dir, derr := os.UserCacheDir() if derr != nil || dir == "" { dir = os.TempDir() } - path := filepath.Join(dir, "quicklogger-shared.txt") - b, err := os.ReadFile(path) + return filepath.Join(dir, "quicklogger-shared.txt") +} + +// readSharedFromCache tries to read the shared text written by the Android activity. +func readSharedFromCache() (string, error) { + b, err := os.ReadFile(sharedTextCachePath()) if err != nil { return "", err } - // best-effort cleanup; ignore errors - _ = os.Remove(path) return string(b), nil } diff --git a/android_shared_stub.go b/android_shared_stub.go index 9ebe1a3..82429f7 100644 --- a/android_shared_stub.go +++ b/android_shared_stub.go @@ -3,3 +3,5 @@ package main func readSharedFromCache() (string, error) { return "", nil } + +func sharedTextCachePath() string { return "" } diff --git a/main.go b/main.go index d913835..dd7fc9c 100644 --- a/main.go +++ b/main.go @@ -52,6 +52,44 @@ func prepareSharedTextLoad(text string, autoLog bool) (sharedTextLoadMode, strin return sharedTextLoadPrefill, text, true } +func clearSharedTextCache() { + if path := sharedTextCachePath(); path != "" { + _ = os.Remove(path) + } +} + +func handleSharedTextLoad( + text string, + autoLog bool, + dir string, + prefill func(string), + focus func(), + resetInput func(), + clearCache func(), + logFn func(string, string) error, + showInfo func(string, string), + showError func(error), +) { + mode, sharedText, ok := prepareSharedTextLoad(text, autoLog) + if !ok { + clearCache() + return + } + if mode == sharedTextLoadAutoLog { + if err := logFn(dir, sharedText); err != nil { + showError(err) + return + } + showInfo("Logged", "Shared text has been logged.") + resetInput() + clearCache() + return + } + prefill(sharedText) + focus() + clearCache() +} + // newInputWidget creates the multi-line text entry with platform-appropriate // wrapping and row count settings. func newInputWidget() *widget.Entry { @@ -152,27 +190,29 @@ func createMainWindow(a fyne.App) fyne.Window { } return } - mode, sharedText, ok := prepareSharedTextLoad(txt, a.Preferences().BoolWithFallback("AutoLogSharedText", false)) - if !ok { - return - } loadingSharedText = true defer func() { loadingSharedText = false }() - if mode == sharedTextLoadAutoLog { - dir := a.Preferences().StringWithFallback("Directory", defaultDirectory) - if err := logEntry(dir, sharedText); err != nil { + handleSharedTextLoad( + txt, + a.Preferences().BoolWithFallback("AutoLogSharedText", false), + a.Preferences().StringWithFallback("Directory", defaultDirectory), + input.SetText, + func() { + window.Canvas().Focus(input) + }, + resetInput, + clearSharedTextCache, + logEntry, + func(title, message string) { + dialog.ShowInformation(title, message, window) + }, + func(err error) { dialog.ShowError(err, window) - return - } - dialog.ShowInformation("Logged", "Shared text has been logged.", window) - resetInput() - return - } - input.SetText(sharedText) - charCount.SetText(fmt.Sprintf("%d chars", len(sharedText))) - window.Canvas().Focus(input) + }, + ) + charCount.SetText(fmt.Sprintf("%d chars", len(input.Text))) } if fyne.CurrentDevice().IsMobile() { diff --git a/main_test.go b/main_test.go index 8d6fac8..399158a 100644 --- a/main_test.go +++ b/main_test.go @@ -1,6 +1,8 @@ package main import ( + "errors" + "fmt" "os" "path/filepath" "strings" @@ -113,3 +115,117 @@ func TestPrepareSharedTextLoadAllowsLongText(t *testing.T) { t.Fatalf("expected original text to be preserved, got %d bytes", len(gotText)) } } + +func TestHandleSharedTextLoadAutoLogSuccessRemovesCache(t *testing.T) { + cacheDir := t.TempDir() + cachePath := filepath.Join(cacheDir, "quicklogger-shared.txt") + if err := os.WriteFile(cachePath, []byte("hello"), 0o644); err != nil { + t.Fatalf("writing cache file: %v", err) + } + + var infoTitle, infoMessage string + var resetCalls, clearCalls int + handleSharedTextLoad( + "hello", + true, + cacheDir, + func(string) { + t.Fatal("prefill should not be called in auto-log mode") + }, + func() { + t.Fatal("focus should not be called in auto-log mode") + }, + func() { + resetCalls++ + }, + func() { + clearCalls++ + if err := os.Remove(cachePath); err != nil && !errors.Is(err, os.ErrNotExist) { + t.Fatalf("removing cache file: %v", err) + } + }, + func(dir, text string) error { + if dir != cacheDir { + t.Fatalf("expected dir %q, got %q", cacheDir, dir) + } + if text != "hello" { + t.Fatalf("expected text %q, got %q", "hello", text) + } + return nil + }, + func(title, message string) { + infoTitle = title + infoMessage = message + }, + func(err error) { + t.Fatalf("unexpected auto-log error: %v", err) + }, + ) + + if resetCalls != 1 { + t.Fatalf("expected resetInput to be called once, got %d", resetCalls) + } + if clearCalls != 1 { + t.Fatalf("expected cache cleanup once, got %d", clearCalls) + } + if infoTitle != "Logged" || infoMessage != "Shared text has been logged." { + t.Fatalf("unexpected info dialog: %q / %q", infoTitle, infoMessage) + } + if _, err := os.Stat(cachePath); !errors.Is(err, os.ErrNotExist) { + t.Fatalf("expected cache file to be removed, stat err=%v", err) + } +} + +func TestHandleSharedTextLoadAutoLogFailureKeepsCache(t *testing.T) { + cacheDir := t.TempDir() + cachePath := filepath.Join(cacheDir, "quicklogger-shared.txt") + if err := os.WriteFile(cachePath, []byte("hello"), 0o644); err != nil { + t.Fatalf("writing cache file: %v", err) + } + + logErr := fmt.Errorf("boom") + var errorCalled bool + var clearCalls int + handleSharedTextLoad( + "hello", + true, + cacheDir, + func(string) { + t.Fatal("prefill should not be called in auto-log mode") + }, + func() { + t.Fatal("focus should not be called in auto-log mode") + }, + func() { + t.Fatal("resetInput should not be called on auto-log failure") + }, + func() { + clearCalls++ + if err := os.Remove(cachePath); err != nil { + t.Fatalf("unexpected cleanup on failure: %v", err) + } + }, + func(string, string) error { + return logErr + }, + func(string, string) { + t.Fatal("info dialog should not be shown on failure") + }, + func(err error) { + if !errors.Is(err, logErr) { + t.Fatalf("expected log error %v, got %v", logErr, err) + } + errorCalled = true + }, + ) + + if !errorCalled { + t.Fatal("expected error dialog callback") + } + if clearCalls != 0 { + t.Fatalf("expected cache to remain on failure, cleanup calls=%d", clearCalls) + } + if _, err := os.Stat(cachePath); err != nil { + t.Fatalf("expected cache file to remain, stat err=%v", err) + } +} -- cgit v1.2.3