Add 'ios/' from commit '2220a0d4f2bb0f0ebca509581c21d6c90359bd14'
git-subtree-dir: ios git-subtree-mainline:52968df567git-subtree-split:2220a0d4f2
This commit is contained in:
174
ios/QueueCube/Views/PlaylistView.swift
Normal file
174
ios/QueueCube/Views/PlaylistView.swift
Normal file
@@ -0,0 +1,174 @@
|
||||
//
|
||||
// PlaylistView.swift
|
||||
// QueueCube
|
||||
//
|
||||
// Created by James Magahern on 3/3/25.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MediaListItem: Identifiable
|
||||
{
|
||||
let _id: String
|
||||
let title: String
|
||||
let filename: String
|
||||
let index: Int?
|
||||
let isCurrent: Bool
|
||||
|
||||
var id: String {
|
||||
_id + filename // temporary: we get duplicate ids from the server sometimes...
|
||||
}
|
||||
|
||||
init(id: String, title: String, filename: String, index: Int? = nil, isCurrent: Bool = false) {
|
||||
self._id = id
|
||||
self.title = title
|
||||
self.filename = filename
|
||||
self.index = index
|
||||
self.isCurrent = isCurrent
|
||||
}
|
||||
}
|
||||
|
||||
enum MediaListMode {
|
||||
case playlist
|
||||
case favorites
|
||||
}
|
||||
|
||||
@Observable
|
||||
class MediaListViewModel
|
||||
{
|
||||
let mode: MediaListMode
|
||||
var isPlaying: Bool = false
|
||||
var items: [MediaListItem] = []
|
||||
|
||||
var onSeek: (MediaListItem) -> Void = { _ in }
|
||||
var onPlay: (MediaListItem) -> Void = { _ in }
|
||||
var onQueue: (MediaListItem) -> Void = { _ in }
|
||||
var onEdit: (MediaListItem) -> Void = { _ in }
|
||||
var onFavorite: (MediaListItem) -> Void = { _ in }
|
||||
|
||||
init(mode: MediaListMode) {
|
||||
self.mode = mode
|
||||
}
|
||||
}
|
||||
|
||||
struct MediaListView: View
|
||||
{
|
||||
@Binding var model: MediaListViewModel
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
if model.items.isEmpty {
|
||||
let title: LocalizedStringKey = switch model.mode {
|
||||
case .playlist: .playlistEmpty
|
||||
case .favorites: .favoritesEmpty
|
||||
}
|
||||
|
||||
contentPlaceholderView(title: title, systemImage: "list.bullet")
|
||||
} else {
|
||||
List($model.items, editActions: .delete) { item in
|
||||
let item = item.wrappedValue
|
||||
let state = item.isCurrent ? (model.isPlaying ? MediaItemCell.State.playing : MediaItemCell.State.paused) : .queued
|
||||
|
||||
Button {
|
||||
switch model.mode {
|
||||
case .playlist:
|
||||
model.onSeek(item)
|
||||
case .favorites:
|
||||
model.onPlay(item)
|
||||
}
|
||||
} label: {
|
||||
MediaItemCell(
|
||||
title: item.title,
|
||||
subtitle: item.filename,
|
||||
state: state
|
||||
)
|
||||
}
|
||||
.listRowBackground((model.mode == .playlist && state != .queued) ? Color.accentColor.opacity(0.10) : nil)
|
||||
.contextMenu {
|
||||
Button(.copyTitle) {
|
||||
UIPasteboard.general.string = item.title
|
||||
}
|
||||
|
||||
Button(.copyURL) {
|
||||
if let url = URL(string: item.filename) {
|
||||
UIPasteboard.general.url = url
|
||||
} else {
|
||||
UIPasteboard.general.string = item.filename
|
||||
}
|
||||
}
|
||||
|
||||
if model.mode == .favorites {
|
||||
Button(.edit) {
|
||||
model.onEdit(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
.swipeActions(edge: .leading) {
|
||||
if model.mode == .favorites {
|
||||
Button {
|
||||
model.onQueue(item)
|
||||
} label: {
|
||||
Image(systemName: "plus.square.on.square")
|
||||
Text(.addToQueue)
|
||||
}
|
||||
.tint(.blue)
|
||||
} else if model.mode == .playlist {
|
||||
Button {
|
||||
model.onFavorite(item)
|
||||
} label: {
|
||||
Image(systemName: "star")
|
||||
Text(.favorite)
|
||||
}
|
||||
.tint(.yellow)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct MediaItemCell: View
|
||||
{
|
||||
let title: String
|
||||
let subtitle: String
|
||||
let state: State
|
||||
|
||||
var body: some View {
|
||||
let icon: String = switch state {
|
||||
case .queued: "play.fill"
|
||||
case .playing: "speaker.wave.3.fill"
|
||||
case .paused: "speaker.fill"
|
||||
}
|
||||
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
.tint(Color.primary)
|
||||
.frame(width: 15.0)
|
||||
.padding(.trailing, 10.0)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(title)
|
||||
.tint(.primary)
|
||||
.lineLimit(1)
|
||||
|
||||
Text(subtitle)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.padding([.top, .bottom], 4.0)
|
||||
}
|
||||
|
||||
// MARK: - Types
|
||||
|
||||
enum State {
|
||||
case queued
|
||||
case playing
|
||||
case paused
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user