161 lines
3.7 KiB
Swift
161 lines
3.7 KiB
Swift
//
|
|
// PlaylistView.swift
|
|
// QueueCube
|
|
//
|
|
// Created by James Magahern on 3/3/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct PlaylistItem: Identifiable
|
|
{
|
|
let index: Int
|
|
let id: String
|
|
let title: String
|
|
let filename: String
|
|
let isCurrent: Bool
|
|
}
|
|
|
|
struct FavoriteItem: Identifiable
|
|
{
|
|
let id: String
|
|
let title: String
|
|
let filename: String
|
|
}
|
|
|
|
@Observable
|
|
class PlaylistViewModel
|
|
{
|
|
var isPlaying: Bool = false
|
|
var items: [PlaylistItem] = []
|
|
|
|
var onSeek: (PlaylistItem) -> Void = { _ in }
|
|
var onDelete: (PlaylistItem) -> Void = { _ in }
|
|
}
|
|
|
|
@Observable
|
|
class FavoritesViewModel
|
|
{
|
|
var items: [FavoriteItem] = []
|
|
|
|
var onPlay: (FavoriteItem) -> Void = { _ in }
|
|
}
|
|
|
|
struct PlaylistView: View
|
|
{
|
|
var model: PlaylistViewModel
|
|
|
|
var body: some View {
|
|
List(model.items) { item in
|
|
PlaylistItemCell(
|
|
title: item.title,
|
|
subtitle: item.filename,
|
|
state: item.isCurrent ? (model.isPlaying ? PlaylistItemCell.State.playing : PlaylistItemCell.State.paused)
|
|
: .queued,
|
|
onLeadingIconClick: { model.onSeek(item) },
|
|
onDeleteButtonClick: { model.onDelete(item) },
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FavoritesView: View
|
|
{
|
|
var model: FavoritesViewModel
|
|
|
|
var body: some View {
|
|
List(model.items) { item in
|
|
FavoriteItemCell(
|
|
title: item.title,
|
|
subtitle: item.filename,
|
|
onPlayButtonClick: { model.onPlay(item) }
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct PlaylistItemCell: View
|
|
{
|
|
let title: String
|
|
let subtitle: String
|
|
let state: State
|
|
|
|
let onLeadingIconClick: () -> Void
|
|
let onDeleteButtonClick: () -> Void
|
|
|
|
var body: some View {
|
|
let icon: String = switch state {
|
|
case .queued: "play.fill"
|
|
case .playing: "speaker.wave.3.fill"
|
|
case .paused: "speaker.fill"
|
|
}
|
|
|
|
HStack {
|
|
Button(action: onLeadingIconClick) { Image(systemName: icon) }
|
|
.buttonStyle(BorderlessButtonStyle())
|
|
.tint(Color.primary)
|
|
.frame(width: 15.0)
|
|
|
|
VStack(alignment: .leading) {
|
|
Text(title)
|
|
.font(.body.bold())
|
|
.lineLimit(1)
|
|
|
|
Text(subtitle)
|
|
.foregroundColor(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
HStack {
|
|
Button(action: onDeleteButtonClick) {
|
|
Image(systemName: "xmark")
|
|
.tint(.red)
|
|
}
|
|
}
|
|
}
|
|
.listRowBackground(state != .queued ? Color.white.opacity(0.15) : nil)
|
|
.padding([.top, .bottom], 8.0)
|
|
}
|
|
|
|
// MARK: - Types
|
|
|
|
enum State {
|
|
case queued
|
|
case playing
|
|
case paused
|
|
}
|
|
}
|
|
|
|
struct FavoriteItemCell: View
|
|
{
|
|
let title: String
|
|
let subtitle: String
|
|
let onPlayButtonClick: () -> Void
|
|
|
|
var body: some View {
|
|
HStack {
|
|
Button(action: onPlayButtonClick) {
|
|
Image(systemName: "play.fill")
|
|
}
|
|
.buttonStyle(BorderlessButtonStyle())
|
|
.tint(Color.primary)
|
|
.frame(width: 15.0)
|
|
|
|
VStack(alignment: .leading) {
|
|
Text(title)
|
|
.font(.body.bold())
|
|
.lineLimit(1)
|
|
|
|
Text(subtitle)
|
|
.foregroundColor(.secondary)
|
|
.lineLimit(1)
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.padding([.top, .bottom], 8.0)
|
|
}
|
|
}
|