From d61f9e50b1d5a949c696cd1fdf8d235e51df1256 Mon Sep 17 00:00:00 2001 From: Trey T Date: Fri, 15 May 2026 15:15:12 -0500 Subject: [PATCH] =?UTF-8?q?Vocab=20Practice=20=E2=80=94=20speaker=20button?= =?UTF-8?q?=20to=20hear=20the=20Spanish=20word?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both vocab session reveal panes now show a speaker button next to the Spanish infinitive. Tapping it speaks the word via the existing SpeechService (Spanish voice), same TTS the Course flashcards use. Flashcard mode: button beside the infinitive in the reveal pane. Multiple choice: button beside the infinitive in the answer feedback. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Vocab/VocabFlashcardPracticeView.swift | 19 ++++++++++++++++--- .../VocabMultipleChoicePracticeView.swift | 19 ++++++++++++++++--- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift b/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift index 66ec806..2737817 100644 --- a/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift +++ b/Conjuga/Conjuga/Views/Practice/Vocab/VocabFlashcardPracticeView.swift @@ -20,6 +20,7 @@ struct VocabFlashcardPracticeView: View { @State private var revealed: Bool = false @State private var exampleByVerbId: [Int: VerbExample] = [:] @State private var generatingExampleForVerbId: Int? = nil + @State private var speech = SpeechService() private var cloudContext: ModelContext { cloudModelContextProvider() } @@ -100,9 +101,21 @@ struct VocabFlashcardPracticeView: View { private func revealedContent(_ verb: Verb) -> some View { VStack(spacing: 18) { - Text(verb.infinitive) - .font(.title.weight(.semibold)) - .multilineTextAlignment(.center) + HStack(spacing: 12) { + Text(verb.infinitive) + .font(.title.weight(.semibold)) + .multilineTextAlignment(.center) + + Button { + speech.speak(verb.infinitive) + } label: { + Image(systemName: "speaker.wave.2.fill") + .font(.title3) + .padding(10) + } + .glassEffect(in: .circle) + .accessibilityLabel("Say it out loud") + } exampleBlock(for: verb) diff --git a/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift b/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift index 2ff7f6a..957e2fa 100644 --- a/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift +++ b/Conjuga/Conjuga/Views/Practice/Vocab/VocabMultipleChoicePracticeView.swift @@ -19,6 +19,7 @@ struct VocabMultipleChoicePracticeView: View { @State private var selectedOption: Verb? = nil @State private var exampleByVerbId: [Int: VerbExample] = [:] @State private var generatingExampleForVerbId: Int? = nil + @State private var speech = SpeechService() private var cloudContext: ModelContext { cloudModelContextProvider() } @@ -109,9 +110,21 @@ struct VocabMultipleChoicePracticeView: View { Text(correct ? "Correct!" : "Not quite") .font(.headline) .foregroundStyle(correct ? .green : .red) - Text(verb.infinitive) - .font(.title2.weight(.semibold)) - .padding(.top, 4) + HStack(spacing: 10) { + Text(verb.infinitive) + .font(.title2.weight(.semibold)) + + Button { + speech.speak(verb.infinitive) + } label: { + Image(systemName: "speaker.wave.2.fill") + .font(.body) + .padding(8) + } + .glassEffect(in: .circle) + .accessibilityLabel("Say it out loud") + } + .padding(.top, 4) } }