Files
Amphlify/Amphlify/UI/Player/QueueView.swift
T

91 lines
4.0 KiB
Swift

import Playback
import SwiftUI
struct QueueView: View {
@Environment(PlayerModel.self) private var player
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 12) {
toggle("Shuffle", "shuffle", isOn: player.state.isShuffled) { player.send(.setShuffle(!player.state.isShuffled)) }
toggle("Repeat", player.state.repeatMode == .one ? "repeat.1" : "repeat", isOn: player.state.repeatMode != .off) {
let modes = RepeatMode.allCases
let index = modes.firstIndex(of: player.state.repeatMode) ?? 0
player.send(.setRepeat(modes[(index + 1) % modes.count]))
}
}
Text("Playing Next")
.font(.headline)
.foregroundStyle(.white)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.top, 18)
.padding(.bottom, 6)
List {
ForEach(player.state.upcoming) { item in
Button {
player.send(.jump(to: item.id))
} label: {
HStack(spacing: 12) {
ArtworkView(artwork: item.track.artwork, shape: .rounded(5)).frame(width: 48, height: 48)
VStack(alignment: .leading, spacing: 2) {
Text(item.track.title).foregroundStyle(.white)
Text(item.track.artistName).font(.subheadline).foregroundStyle(.white.opacity(0.6))
}
.lineLimit(1)
Spacer(minLength: 0)
Image(systemName: "line.3.horizontal").foregroundStyle(.white.opacity(0.3))
}
.contentShape(.rect)
}
.buttonStyle(.plain)
.listRowBackground(Color.clear)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets(top: 4, leading: 0, bottom: 4, trailing: 0))
}
.onMove(perform: move)
.onDelete { offsets in
offsets.map { player.state.upcoming[$0].id }.forEach { player.send(.remove($0)) }
}
}
.listStyle(.plain)
.scrollContentBackground(.hidden)
.scrollIndicators(.hidden)
.overlay {
if player.state.upcoming.isEmpty {
Text("Nothing up next").foregroundStyle(.white.opacity(0.5))
}
}
.mask {
LinearGradient(
stops: [.init(color: .black, location: 0.9), .init(color: .clear, location: 1)],
startPoint: .top,
endPoint: .bottom
)
}
}
}
private func move(from offsets: IndexSet, to destination: Int) {
let upcoming = player.state.upcoming
guard let from = offsets.first else { return }
var reordered = upcoming
reordered.move(fromOffsets: offsets, toOffset: destination)
guard let landed = reordered.firstIndex(where: { $0.id == upcoming[from].id }) else { return }
player.send(.move(item: upcoming[from].id, after: landed > 0 ? reordered[landed - 1].id : nil))
}
private func toggle(_ title: LocalizedStringKey, _ systemImage: String, isOn: Bool, action: @escaping () -> Void) -> some View {
Button(action: action) {
Label(title, systemImage: systemImage)
.labelStyle(.iconOnly)
.font(.subheadline.weight(.semibold))
.foregroundStyle(isOn ? Color.black.opacity(0.8) : .white)
.frame(maxWidth: .infinity, minHeight: 38)
.background(.white.opacity(isOn ? 0.55 : 0.12), in: .capsule)
.contentShape(.rect)
}
.buttonStyle(.plain)
.accessibilityAddTraits(isOn ? .isSelected : [])
}
}