110 lines
3.5 KiB
Swift
110 lines
3.5 KiB
Swift
import SwiftUI
|
|
|
|
struct MiniPlayerView: View {
|
|
@Environment(AppModel.self) private var model
|
|
@Binding var isMiniPlayer: Bool
|
|
|
|
var body: some View {
|
|
HStack(spacing: 12) {
|
|
Image(nsImage: model.playback.currentArtwork)
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(width: 64, height: 64)
|
|
.background(.quaternary)
|
|
.clipShape(RoundedRectangle(cornerRadius: 8))
|
|
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(model.playback.currentStation?.name ?? String(localized: "No station selected"))
|
|
.font(.headline)
|
|
.lineLimit(1)
|
|
Text(miniPlayerText)
|
|
.font(.caption)
|
|
.foregroundStyle(playerDetailColor)
|
|
.lineLimit(1)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
Button {
|
|
model.playback.togglePlayPause()
|
|
} label: {
|
|
Image(systemName: model.playback.isPlaying ? "pause.fill" : "play.fill")
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.controlSize(.small)
|
|
.disabled(model.playback.currentStation == nil)
|
|
|
|
Button {
|
|
model.playback.stop()
|
|
} label: {
|
|
Image(systemName: "stop.fill")
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.controlSize(.small)
|
|
|
|
HStack(spacing: 4) {
|
|
Image(systemName: "speaker.wave.1")
|
|
.font(.caption)
|
|
Slider(value: Binding(
|
|
get: { model.playback.volume },
|
|
set: { model.setVolume($0) }
|
|
), in: 0...1)
|
|
.frame(width: 80)
|
|
Image(systemName: "speaker.wave.3")
|
|
.font(.caption)
|
|
}
|
|
.foregroundStyle(.secondary)
|
|
|
|
Button {
|
|
isMiniPlayer = false
|
|
} label: {
|
|
Image(systemName: "arrow.up.left.and.arrow.down.right")
|
|
}
|
|
.buttonStyle(.bordered)
|
|
.controlSize(.small)
|
|
.help("Show full window")
|
|
}
|
|
.padding(12)
|
|
.frame(height: 110)
|
|
.background(.bar)
|
|
}
|
|
|
|
private var miniPlayerText: String {
|
|
if let detail = model.playback.state.detail {
|
|
return "\(model.playback.state.title): \(detail)"
|
|
}
|
|
switch model.playback.state {
|
|
case .connecting, .buffering, .failed:
|
|
return model.playback.state.title
|
|
default:
|
|
break
|
|
}
|
|
if !model.playback.currentTrackTitle.isEmpty && !model.playback.currentTrackArtist.isEmpty {
|
|
return "\(model.playback.currentTrackArtist) - \(model.playback.currentTrackTitle)"
|
|
}
|
|
if !model.playback.currentTrackTitle.isEmpty {
|
|
return model.playback.currentTrackTitle
|
|
}
|
|
if !model.playback.currentTrackArtist.isEmpty {
|
|
return model.playback.currentTrackArtist
|
|
}
|
|
if let station = model.playback.currentStation, !station.tags.isEmpty {
|
|
return station.tags
|
|
}
|
|
return String(localized: "Live radio")
|
|
}
|
|
|
|
private var playerDetailColor: Color {
|
|
if case .failed = model.playback.state {
|
|
return .red
|
|
}
|
|
if case .connecting = model.playback.state {
|
|
return .secondary
|
|
}
|
|
if case .buffering = model.playback.state {
|
|
return .orange
|
|
}
|
|
return .secondary
|
|
}
|
|
}
|