import SwiftUI struct TrackEditorView: View { let song: Song @Environment(\.dismiss) private var dismiss @State private var title: String @State private var artist: String @State private var album: String @State private var albumArtist: String @State private var genre: String @State private var year: String @State private var trackNumber: String @State private var discNumber: String @State private var comment: String @State private var isSaving = false @State private var errorMessage: String? @State private var showSuccess = false @ObservedObject private var settings = CompanionSettings.shared private let api = CompanionAPIService() private let accentPink = Color(red: 1.0, green: 0.176, blue: 0.333) init(song: Song) { self.song = song _title = State(initialValue: song.title) _artist = State(initialValue: song.artist ?? "") _album = State(initialValue: song.album ?? "") _albumArtist = State(initialValue: "") _genre = State(initialValue: song.genre ?? "") _year = State(initialValue: song.year != nil ? "\(song.year!)" : "") _trackNumber = State(initialValue: song.track != nil ? "\(song.track!)" : "") _discNumber = State(initialValue: song.discNumber != nil ? "\(song.discNumber!)" : "") _comment = State(initialValue: "") } var body: some View { NavigationStack { List { if !settings.isEnabled { Section { VStack(spacing: 8) { Image(systemName: "exclamationmark.triangle") .font(.system(size: 28)) .foregroundColor(.yellow) Text("Companion API Required") .font(.system(size: 15, weight: .medium)) Text("Configure the Companion API in Settings to edit track metadata remotely.") .font(.system(size: 13)) .foregroundColor(.gray) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) .padding(.vertical, 12) } } else { // File info (read-only) Section { infoRow("File", song.path ?? song.id) if let suffix = song.suffix { infoRow("Format", suffix.uppercased()) } if let bitRate = song.bitRate { infoRow("Bit Rate", "\(bitRate) kbps") } } header: { Text("File Info") } // Editable fields Section { editField("Title", text: $title) editField("Artist", text: $artist) editField("Album", text: $album) editField("Album Artist", text: $albumArtist) } header: { Text("Basic Info") } Section { editField("Genre", text: $genre) editField("Year", text: $year, keyboard: .numberPad) editField("Track #", text: $trackNumber, keyboard: .numberPad) editField("Disc #", text: $discNumber, keyboard: .numberPad) } header: { Text("Details") } Section { TextEditor(text: $comment) .frame(minHeight: 60) .font(.system(size: 14)) } header: { Text("Comment") } // Error / Success if let error = errorMessage { Section { Text(error) .font(.system(size: 13)) .foregroundColor(.red) } } } } .navigationTitle("Edit Tags") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button("Cancel") { dismiss() } .foregroundColor(.gray) } ToolbarItem(placement: .navigationBarTrailing) { if settings.isEnabled { Button(action: save) { if isSaving { ProgressView() .tint(accentPink) } else { Text("Save") .fontWeight(.semibold) .foregroundColor(accentPink) } } .disabled(isSaving || song.path == nil) } } } .overlay { if showSuccess { VStack(spacing: 12) { Image(systemName: "checkmark.circle.fill") .font(.system(size: 44)) .foregroundColor(.green) Text("Tags Updated") .font(.system(size: 16, weight: .medium)) .foregroundColor(.white) } .padding(30) .background(.ultraThinMaterial) .cornerRadius(16) .transition(.scale.combined(with: .opacity)) } } } } // MARK: - Save private func save() { guard let path = song.path else { return } isSaving = true errorMessage = nil Task { do { let request = MetadataEditRequest( relativePath: path, title: title.isEmpty ? nil : title, artist: artist.isEmpty ? nil : artist, album: album.isEmpty ? nil : album ) try await api.editMetadata(request) await MainActor.run { isSaving = false withAnimation { showSuccess = true } DebugLogger.shared.log("Tags updated: \(title) — \(artist)", category: "Companion") } try? await Task.sleep(for: .seconds(1.2)) await MainActor.run { dismiss() } } catch { await MainActor.run { isSaving = false errorMessage = error.localizedDescription DebugLogger.shared.log("Tag edit failed: \(error.localizedDescription)", category: "Companion") } } } } // MARK: - Helpers private func editField(_ label: String, text: Binding, keyboard: UIKeyboardType = .default) -> some View { HStack { Text(label) .foregroundColor(.gray) .frame(width: 90, alignment: .leading) TextField(label, text: text) .keyboardType(keyboard) .multilineTextAlignment(.trailing) } .font(.system(size: 14)) } private func infoRow(_ label: String, _ value: String) -> some View { HStack { Text(label) .foregroundColor(.gray) Spacer() Text(value) .foregroundColor(.white.opacity(0.7)) .lineLimit(1) } .font(.system(size: 13)) } }