TrackRow.swift 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import SwiftUI
  2. /// Compact track row for track lists.
  3. struct TrackRow: View {
  4. let track: Track
  5. var body: some View {
  6. HStack(spacing: 8) {
  7. // Album art placeholder / format badge
  8. ZStack {
  9. RoundedRectangle(cornerRadius: 4)
  10. .fill(trackColor.opacity(0.15))
  11. .frame(width: 32, height: 32)
  12. Image(systemName: "music.note")
  13. .font(.caption)
  14. .foregroundStyle(trackColor)
  15. }
  16. VStack(alignment: .leading, spacing: 1) {
  17. HStack(spacing: 4) {
  18. Text(track.title)
  19. .lineLimit(1)
  20. .font(.body)
  21. trackStateIcon
  22. }
  23. if !track.artist.isEmpty {
  24. Text(track.artist)
  25. .lineLimit(1)
  26. .font(.caption)
  27. .foregroundStyle(.secondary)
  28. }
  29. }
  30. }
  31. }
  32. /// Priority-ordered state icon for upload/download/cloud status.
  33. @ViewBuilder
  34. private var trackStateIcon: some View {
  35. if track.uploadState == .uploading {
  36. Image(systemName: "arrow.up.circle.fill")
  37. .font(.system(size: 11))
  38. .foregroundStyle(.orange)
  39. } else if track.uploadState == .error {
  40. Image(systemName: "exclamationmark.circle.fill")
  41. .font(.system(size: 11))
  42. .foregroundStyle(.red)
  43. } else if track.isCloud && track.downloadState == .downloading {
  44. Image(systemName: "arrow.down.circle")
  45. .font(.system(size: 11))
  46. .foregroundStyle(Color.accentColor)
  47. } else if track.isCloud && track.downloadState == .error {
  48. Image(systemName: "exclamationmark.circle.fill")
  49. .font(.system(size: 11))
  50. .foregroundStyle(.red)
  51. } else if track.isCloud && (track.downloadState == .downloaded || track.localCachePath != nil) {
  52. Image(systemName: "arrow.down.circle.fill")
  53. .font(.system(size: 11))
  54. .foregroundStyle(.green)
  55. } else if track.uploadState == .uploaded {
  56. Image(systemName: "arrow.up.circle.fill")
  57. .font(.system(size: 11))
  58. .foregroundStyle(.green)
  59. } else if track.isCloud {
  60. Image(systemName: "cloud.fill")
  61. .font(.system(size: 11))
  62. .foregroundStyle(Color.accentColor.opacity(0.85))
  63. }
  64. }
  65. private var trackColor: Color {
  66. if let hex = track.color {
  67. return Color(hex: hex) ?? .accentColor
  68. }
  69. return .accentColor
  70. }
  71. }