Create BearWave macOS radio-first app
This commit is contained in:
99
Sources/BearWaveCore/Views/PlayerBarView.swift
Normal file
99
Sources/BearWaveCore/Views/PlayerBarView.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
import SwiftUI
|
||||
|
||||
struct PlayerBarView: View {
|
||||
@Environment(AppModel.self) private var model
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 14) {
|
||||
Image(nsImage: model.playback.currentArtwork)
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 46, height: 46)
|
||||
.background(.quaternary)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(model.playback.currentStation?.name ?? model.library.selectedStation?.name ?? String(localized: "No station selected"))
|
||||
.font(.headline)
|
||||
.lineLimit(1)
|
||||
Text(nowPlayingText)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(playerDetailColor)
|
||||
.lineLimit(1)
|
||||
.help(nowPlayingText)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
radioControls
|
||||
|
||||
HStack {
|
||||
Image(systemName: "speaker.wave.1")
|
||||
Slider(value: Binding(
|
||||
get: { model.playback.volume },
|
||||
set: { model.setVolume($0) }
|
||||
), in: 0...1)
|
||||
.frame(width: 150)
|
||||
Image(systemName: "speaker.wave.3")
|
||||
}
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
.padding(12)
|
||||
.background(.bar)
|
||||
}
|
||||
|
||||
private var radioControls: some View {
|
||||
HStack(spacing: 8) {
|
||||
Button {
|
||||
model.playback.togglePlayPause()
|
||||
} label: {
|
||||
Label(model.playback.isPlaying ? "Pause" : "Play", systemImage: model.playback.isPlaying ? "pause.fill" : "play.fill")
|
||||
}
|
||||
.disabled(model.playback.currentStation == nil)
|
||||
|
||||
Button {
|
||||
model.playback.stop()
|
||||
} label: {
|
||||
Label("Stop", systemImage: "stop.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var nowPlayingText: 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user