Files
Spanish/Conjuga/Conjuga/Views/Practice/Lyrics/LyricsLibraryView.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

97 lines
2.9 KiB
Swift

import SwiftUI
import SharedModels
import SwiftData
struct LyricsLibraryView: View {
@Query(sort: \SavedSong.savedDate, order: .reverse) private var songs: [SavedSong]
@State private var showingSearch = false
var body: some View {
Group {
if songs.isEmpty {
ContentUnavailableView(
"No Songs Yet",
systemImage: "music.note.list",
description: Text("Tap + to search for Spanish song lyrics.")
)
} else {
List {
ForEach(songs) { song in
NavigationLink {
LyricsReaderView(song: song)
} label: {
SongRowView(song: song)
}
}
.onDelete(perform: deleteSongs)
}
}
}
.navigationTitle("Lyrics")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
showingSearch = true
} label: {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingSearch) {
NavigationStack {
LyricsSearchView {
showingSearch = false
}
}
}
}
@Environment(\.modelContext) private var modelContext
private func deleteSongs(at offsets: IndexSet) {
for index in offsets {
modelContext.delete(songs[index])
}
try? modelContext.save()
}
}
// MARK: - Song Row
private struct SongRowView: View {
let song: SavedSong
var body: some View {
HStack(spacing: 12) {
if !song.albumArtURL.isEmpty, let url = URL(string: song.albumArtURL) {
AsyncImage(url: url) { image in
image.resizable().scaledToFill()
} placeholder: {
RoundedRectangle(cornerRadius: 6)
.fill(.fill.quaternary)
}
.frame(width: 50, height: 50)
.clipShape(RoundedRectangle(cornerRadius: 6))
} else {
Image(systemName: "music.note")
.font(.title2)
.foregroundStyle(.secondary)
.frame(width: 50, height: 50)
.background(.fill.quaternary, in: RoundedRectangle(cornerRadius: 6))
}
VStack(alignment: .leading, spacing: 2) {
Text(song.title)
.font(.subheadline.weight(.semibold))
.lineLimit(1)
Text(song.artist)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
}
}
.padding(.vertical, 2)
}
}