Files
Spanish/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsReaderView.swift
Trey t 636193fae1 Fix lyrics navigation, translation line alignment, and store reset
Navigation: present search as a sheet from library (avoids nested
NavigationStack), use view-based NavigationLink for song rows (fixes
double-push from duplicate navigationDestination).

Translation: Apple Translation inserts a blank line after every
translated line. Strip all blanks from the EN output, then re-insert
them at the same positions where the original ES has blanks. Result
is 1:1 line pairing between Spanish and English.

Store reset: revert localStoreResetVersion bump — adding SavedSong
is a lightweight SwiftData migration, no store nuke needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 23:08:32 -05:00

98 lines
3.4 KiB
Swift

import SwiftUI
import SharedModels
struct LyricsReaderView: View {
let song: SavedSong
var body: some View {
ScrollView {
VStack(spacing: 20) {
headerSection
lyricsBody
}
.padding()
.adaptiveContainer()
}
.navigationTitle(song.title)
.navigationBarTitleDisplayMode(.inline)
}
// MARK: - Header
private var headerSection: some View {
VStack(spacing: 10) {
if !song.albumArtURL.isEmpty, let url = URL(string: song.albumArtURL) {
AsyncImage(url: url) { image in
image.resizable().scaledToFill()
} placeholder: {
RoundedRectangle(cornerRadius: 12)
.fill(.fill.quaternary)
}
.frame(width: 160, height: 160)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
Text(song.title)
.font(.title2.weight(.bold))
.multilineTextAlignment(.center)
Text(song.artist)
.font(.subheadline)
.foregroundStyle(.secondary)
if !song.appleMusicURL.isEmpty, let url = URL(string: song.appleMusicURL) {
Link(destination: url) {
Label("Open in Apple Music", systemImage: "apple.logo")
.font(.caption.weight(.medium))
}
.tint(.pink)
}
}
}
// MARK: - Lyrics Body
private var lyricsBody: some View {
let spanishLines = song.lyricsES.components(separatedBy: "\n")
let englishLines = song.lyricsEN.components(separatedBy: "\n")
let lineCount = max(spanishLines.count, englishLines.count)
let _ = {
print("[LyricsReader] ES lines: \(spanishLines.count), EN lines: \(englishLines.count), rendering: \(lineCount)")
for i in 0..<min(15, lineCount) {
let es = i < spanishLines.count ? spanishLines[i] : "(none)"
let en = i < englishLines.count ? englishLines[i] : "(none)"
print(" [\(i)] ES: \(es.isEmpty ? "(blank)" : es)")
print(" EN: \(en.isEmpty ? "(blank)" : en)")
}
}()
return VStack(alignment: .leading, spacing: 0) {
ForEach(0..<lineCount, id: \.self) { index in
let es = index < spanishLines.count ? spanishLines[index] : ""
let en = index < englishLines.count ? englishLines[index] : ""
if es.trimmingCharacters(in: .whitespaces).isEmpty &&
en.trimmingCharacters(in: .whitespaces).isEmpty {
// Blank line = section divider
Spacer().frame(height: 20)
} else {
VStack(alignment: .leading, spacing: 2) {
if !es.isEmpty {
Text(es)
.font(.body.weight(.medium))
}
if !en.isEmpty {
Text(en)
.font(.caption)
.foregroundStyle(.secondary)
}
}
.padding(.vertical, 4)
}
}
}
.padding()
.background(.fill.quinary, in: RoundedRectangle(cornerRadius: 12))
}
}