feat: add editable playback queue
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
import Foundation
|
||||
|
||||
struct PlaybackQueue {
|
||||
struct Item: Identifiable {
|
||||
let id = UUID()
|
||||
let url: URL
|
||||
}
|
||||
|
||||
private(set) var items: [Item] = []
|
||||
private(set) var currentID: UUID?
|
||||
var currentURL: URL? { items.first { $0.id == currentID }?.url }
|
||||
private var currentIndex: Int? { items.firstIndex { $0.id == currentID } }
|
||||
var canPrevious: Bool { currentIndex.map { $0 > 0 } ?? false }
|
||||
var canNext: Bool { currentIndex.map { $0 + 1 < items.count } ?? false }
|
||||
|
||||
mutating func replace(_ urls: [URL]) {
|
||||
items = sortedFiles(urls).map { Item(url: $0) }
|
||||
currentID = items.first?.id
|
||||
}
|
||||
|
||||
mutating func append(_ urls: [URL]) {
|
||||
items.append(contentsOf: sortedFiles(urls).map { Item(url: $0) })
|
||||
}
|
||||
|
||||
mutating func select(_ id: UUID) {
|
||||
guard items.contains(where: { $0.id == id }) else { return }
|
||||
currentID = id
|
||||
}
|
||||
|
||||
mutating func advance(_ offset: Int) -> URL? {
|
||||
guard let index = currentIndex else { return nil }
|
||||
let (target, overflow) = index.addingReportingOverflow(offset)
|
||||
guard !overflow, items.indices.contains(target) else { return nil }
|
||||
currentID = items[target].id
|
||||
return items[target].url
|
||||
}
|
||||
|
||||
mutating func remove(_ id: UUID) -> URL? {
|
||||
guard let index = items.firstIndex(where: { $0.id == id }) else { return nil }
|
||||
let wasCurrent = currentID == id
|
||||
items.remove(at: index)
|
||||
guard wasCurrent else { return nil }
|
||||
currentID = items.indices.contains(index) ? items[index].id : nil
|
||||
return currentURL
|
||||
}
|
||||
|
||||
mutating func move(fromOffsets offsets: IndexSet, toOffset destination: Int) {
|
||||
guard !offsets.isEmpty, offsets.allSatisfy(items.indices.contains),
|
||||
(0...items.count).contains(destination) else { return }
|
||||
let moving = offsets.map { items[$0] }
|
||||
for index in offsets.reversed() { items.remove(at: index) }
|
||||
let insertion = destination - offsets.filter { $0 < destination }.count
|
||||
items.insert(contentsOf: moving, at: insertion)
|
||||
}
|
||||
|
||||
private func sortedFiles(_ urls: [URL]) -> [URL] {
|
||||
urls.filter(\.isFileURL).sorted {
|
||||
let order = $0.lastPathComponent.localizedStandardCompare($1.lastPathComponent)
|
||||
return order == .orderedSame ? $0.path < $1.path : order == .orderedAscending
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user