Create BearWave macOS radio-first app
This commit is contained in:
109
Sources/BearWaveCore/Views/MiniPlayerView.swift
Normal file
109
Sources/BearWaveCore/Views/MiniPlayerView.swift
Normal file
@@ -0,0 +1,109 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user