From 95dd36d28d18615ad3f8dd7122a404850dcb39f8 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 6 Apr 2026 10:47:33 +0300 Subject: refactor: decompose gui.Application god object into focused services Extract CardService (file I/O, persistence, card directory management) and GenerationOrchestrator (audio/image/phonetics generation) from the 2852-line Application struct. Application is now thin UI event-wiring. Also split all functions over 50 lines into focused helpers throughout app.go. Co-Authored-By: Claude Sonnet 4.6 --- internal/gui/persistence.go | 105 +++++++------------------------------------- 1 file changed, 17 insertions(+), 88 deletions(-) (limited to 'internal/gui/persistence.go') diff --git a/internal/gui/persistence.go b/internal/gui/persistence.go index 0919974..e97a379 100644 --- a/internal/gui/persistence.go +++ b/internal/gui/persistence.go @@ -2,110 +2,39 @@ package gui import ( "fmt" - "os" - "path/filepath" - - "fyne.io/fyne/v2" - - "codeberg.org/snonux/totalrecall/internal" ) -// ensureWordDirectoryAndMetadata creates a new card directory and writes word metadata. +// ensureWordDirectoryAndMetadata creates a new card directory and writes word +// metadata. Delegates to CardService. func (a *Application) ensureWordDirectoryAndMetadata(word string) (string, error) { - cardID := internal.GenerateCardID(word) - wordDir := filepath.Join(a.config.OutputDir, cardID) - if err := os.MkdirAll(wordDir, 0755); err != nil { - return "", fmt.Errorf("failed to create card directory: %w", err) - } - - metadataFile := filepath.Join(wordDir, "word.txt") - if err := os.WriteFile(metadataFile, []byte(word), 0644); err != nil { - return "", fmt.Errorf("failed to save word metadata: %w", err) - } - - return wordDir, nil + return a.getCardService().EnsureWordDirectoryAndMetadata(word) } -// ensureCardDirectory ensures a card directory exists for the given word and returns its path. +// ensureCardDirectory ensures a card directory exists for the given word. +// Delegates to CardService. func (a *Application) ensureCardDirectory(word string) (string, error) { - wordDir := a.findCardDirectory(word) - if wordDir != "" { - return wordDir, nil - } - - return a.ensureWordDirectoryAndMetadata(word) + return a.getCardService().EnsureCardDirectory(word) } -// saveTranslation saves the current translation to a file. +// saveTranslation saves the current translation to disk. +// Delegates to CardService. func (a *Application) saveTranslation() { - if a.currentWord == "" || a.currentTranslation == "" { - return - } - - wordDir := a.findCardDirectory(a.currentWord) - if wordDir == "" { - newWordDir, err := a.ensureWordDirectoryAndMetadata(a.currentWord) - if err != nil { - a.showError(err) - return - } - wordDir = newWordDir - } - - translationFile := filepath.Join(wordDir, "translation.txt") - content := fmt.Sprintf("%s = %s\n", a.currentWord, a.currentTranslation) - if err := os.WriteFile(translationFile, []byte(content), 0644); err != nil { - a.showError(fmt.Errorf("failed to save translation: %w", err)) + if err := a.getCardService().SaveTranslation(a.currentWord, a.currentTranslation); err != nil { + a.showError(err) } } -// saveImagePrompt saves the current image prompt to a file. +// saveImagePrompt is retained for compatibility but is currently a no-op. +// The image prompt is saved by the image generation callback when the image +// is generated, so there is no need to save it separately here. func (a *Application) saveImagePrompt() { - // With timestamp-based card IDs, we can't update existing prompts. - // The prompt is saved when the image is generated. - // This function is kept for compatibility but does nothing. + // No-op: the prompt is saved as soon as it is generated via the callback. } -// savePhoneticInfo saves the phonetic information to a file. +// savePhoneticInfo saves the current phonetic information to disk. +// Delegates to CardService. func (a *Application) savePhoneticInfo() { - phoneticText := a.currentPhonetic - if a.currentWord == "" || phoneticText == "" || phoneticText == "Failed to fetch phonetic information" { - return - } - - wordDir := a.findCardDirectory(a.currentWord) - if wordDir == "" { - newWordDir, err := a.ensureWordDirectoryAndMetadata(a.currentWord) - if err != nil { - a.showError(err) - return - } - wordDir = newWordDir - } - - phoneticFile := filepath.Join(wordDir, "phonetic.txt") - if err := os.WriteFile(phoneticFile, []byte(phoneticText), 0644); err != nil { + if err := a.getCardService().SavePhoneticInfo(a.currentWord, a.currentPhonetic); err != nil { a.showError(fmt.Errorf("failed to save phonetic info: %w", err)) } } - -// loadPhoneticInfo loads phonetic information from a file if it exists. -func (a *Application) loadPhoneticInfo(word string) { - wordDir := a.findCardDirectory(word) - if wordDir == "" { - return - } - - phoneticFile := filepath.Join(wordDir, "phonetic.txt") - if data, err := os.ReadFile(phoneticFile); err == nil { - phoneticText := string(data) - a.currentPhonetic = phoneticText - fyne.Do(func() { - if phoneticText != "" { - a.audioPlayer.SetPhonetic(phoneticText) - } else { - a.audioPlayer.SetPhonetic("") - } - }) - } -} -- cgit v1.2.3