add mobile androida and ios apps
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import SwiftUI
|
||||
|
||||
struct NowPlayingView: View {
|
||||
@StateObject private var viewModel = NowPlayingController()
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Spacer()
|
||||
coverArtSection
|
||||
trackInfoSection
|
||||
.padding(.horizontal)
|
||||
Spacer()
|
||||
playbackControls
|
||||
volumeSlider
|
||||
.padding(.horizontal)
|
||||
statusFooter
|
||||
.padding()
|
||||
}
|
||||
.onAppear {
|
||||
viewModel.connect()
|
||||
}
|
||||
.onChange(of: viewModel.volume) { _, _ in
|
||||
// slider binding handles this
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sections
|
||||
|
||||
private var coverArtSection: some View {
|
||||
ZStack {
|
||||
if let image = viewModel.coverArt {
|
||||
Image(uiImage: image)
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 280, height: 280)
|
||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
||||
} else {
|
||||
RoundedRectangle(cornerRadius: 16)
|
||||
.fill(Color.gray.opacity(0.2))
|
||||
.frame(width: 280, height: 280)
|
||||
.overlay(
|
||||
Image(systemName: "music.note")
|
||||
.font(.system(size: 60))
|
||||
.foregroundColor(.gray.opacity(0.5))
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 40)
|
||||
}
|
||||
|
||||
private var trackInfoSection: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text(viewModel.currentTrack?.name ?? "No track")
|
||||
.font(.title.bold())
|
||||
.foregroundColor(.white)
|
||||
|
||||
if let artists = viewModel.currentTrack?.artistNames, !artists.isEmpty {
|
||||
Text(artists.joined(separator: ", "))
|
||||
.font(.title3)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
if let album = viewModel.currentTrack?.album?.name {
|
||||
Text(album)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.gray.opacity(0.7))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var playbackControls: some View {
|
||||
HStack(spacing: 40) {
|
||||
Button(action: {}) {
|
||||
Image(systemName: "backward.fill")
|
||||
.font(.title)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
Button(action: { viewModel.togglePlay() }) {
|
||||
Image(systemName: viewModel.isPlaying ? "pause.circle.fill" : "play.circle.fill")
|
||||
.font(.system(size: 72))
|
||||
.foregroundColor(.white)
|
||||
}
|
||||
|
||||
Button(action: {}) {
|
||||
Image(systemName: "forward.fill")
|
||||
.font(.title)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 30)
|
||||
}
|
||||
|
||||
private var volumeSlider: some View {
|
||||
HStack {
|
||||
Image(systemName: "speaker.fill")
|
||||
.foregroundColor(.gray)
|
||||
Slider(value: $viewModel.volume, in: 0...100)
|
||||
.onChange(of: viewModel.volume) { newValue in
|
||||
viewModel.setVolume(newValue)
|
||||
}
|
||||
Image(systemName: "speaker.wave.3.fill")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
|
||||
private var statusFooter: some View {
|
||||
HStack {
|
||||
Circle()
|
||||
.fill(viewModel.isPlaying ? Color.green : Color.red)
|
||||
.frame(width: 8, height: 8)
|
||||
Text(viewModel.connectionStatus)
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
|
||||
Spacer()
|
||||
|
||||
NavigationLink(destination: SettingsView()) {
|
||||
Image(systemName: "gear")
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NowPlayingView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NowPlayingView()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user