implements editing items in favorites
This commit is contained in:
@@ -139,6 +139,14 @@ struct API
|
|||||||
.execute()
|
.execute()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func renameFavorite(mediaURL: String, title: String) async throws {
|
||||||
|
try await request()
|
||||||
|
.pathString("/favorites/\(mediaURL.uriEncoded())/title")
|
||||||
|
.body([ "title": title ])
|
||||||
|
.method(.put)
|
||||||
|
.execute()
|
||||||
|
}
|
||||||
|
|
||||||
public func delete(index: Int) async throws {
|
public func delete(index: Int) async throws {
|
||||||
try await request()
|
try await request()
|
||||||
.path("/playlist/\(index)")
|
.path("/playlist/\(index)")
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ struct RequestBuilder
|
|||||||
|
|
||||||
enum HTTPMethod: String {
|
enum HTTPMethod: String {
|
||||||
case get = "GET"
|
case get = "GET"
|
||||||
|
case put = "PUT"
|
||||||
case post = "POST"
|
case post = "POST"
|
||||||
case delete = "DELETE"
|
case delete = "DELETE"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ADD_TO_QUEUE" : {
|
"ADD_TO_QUEUE" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Add to Queue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"CANCEL" : {
|
"CANCEL" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -127,6 +134,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"EDIT_ITEM" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Edit Item"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ENTER_MANUALLY" : {
|
"ENTER_MANUALLY" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@@ -323,8 +340,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"TODO" : {
|
"TITLE" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Title"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"UNABLE_TO_CONNECT" : {
|
"UNABLE_TO_CONNECT" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -336,6 +360,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"URL" : {
|
||||||
|
"localizations" : {
|
||||||
|
"en" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "URL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"VALIDATING" : {
|
"VALIDATING" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import SwiftUI
|
|||||||
|
|
||||||
extension LocalizedStringKey
|
extension LocalizedStringKey
|
||||||
{
|
{
|
||||||
static let serverURL = LocalizedStringKey("SERVER_URL")
|
static let serverURL = LocalizedStringKey("SERVER_URL")
|
||||||
static let settings = LocalizedStringKey("SETTINGS")
|
static let settings = LocalizedStringKey("SETTINGS")
|
||||||
static let settings_ = LocalizedStringKey("SETTINGS_ELLIPSES")
|
static let settings_ = LocalizedStringKey("SETTINGS_ELLIPSES")
|
||||||
static let done = LocalizedStringKey("DONE")
|
static let done = LocalizedStringKey("DONE")
|
||||||
@@ -41,6 +41,9 @@ extension LocalizedStringKey
|
|||||||
static let copyTitle = LocalizedStringKey("COPY_TITLE")
|
static let copyTitle = LocalizedStringKey("COPY_TITLE")
|
||||||
static let copyURL = LocalizedStringKey("COPY_URL")
|
static let copyURL = LocalizedStringKey("COPY_URL")
|
||||||
static let edit = LocalizedStringKey("EDIT")
|
static let edit = LocalizedStringKey("EDIT")
|
||||||
|
static let editItem = LocalizedStringKey("EDIT_ITEM")
|
||||||
static let addToQueue = LocalizedStringKey("ADD_TO_QUEUE")
|
static let addToQueue = LocalizedStringKey("ADD_TO_QUEUE")
|
||||||
static let notPlaying = LocalizedStringKey("NOT_PLAYING")
|
static let notPlaying = LocalizedStringKey("NOT_PLAYING")
|
||||||
|
static let url = LocalizedStringKey("URL")
|
||||||
|
static let title = LocalizedStringKey("TITLE")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ struct ContentView: View
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $model.isEditSheetPresented) {
|
.sheet(isPresented: $model.isEditSheetPresented) {
|
||||||
Text("TODO")
|
EditItemView(model: $model.editMediaViewModel)
|
||||||
|
.presentationBackground(.regularMaterial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
60
QueueCube/Views/EditItemView.swift
Normal file
60
QueueCube/Views/EditItemView.swift
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// EditItemView.swift
|
||||||
|
// QueueCube
|
||||||
|
//
|
||||||
|
// Created by James Magahern on 6/20/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@Observable
|
||||||
|
class EditItemViewModel
|
||||||
|
{
|
||||||
|
var mediaURL: String = ""
|
||||||
|
var title: String = ""
|
||||||
|
|
||||||
|
var onDone: (EditItemViewModel) -> Void = { _ in }
|
||||||
|
var onCancel: (EditItemViewModel) -> Void = { _ in }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EditItemView: View
|
||||||
|
{
|
||||||
|
@Binding var model: EditItemViewModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationStack {
|
||||||
|
Form {
|
||||||
|
Section(.url) {
|
||||||
|
TextField(.url, text: $model.mediaURL)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.disabled(true) // editing URL not yet supported by server
|
||||||
|
.contextMenu {
|
||||||
|
Button(.copyURL) {
|
||||||
|
UIPasteboard.general.string = model.mediaURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section(.title) {
|
||||||
|
TextField(.title, text: $model.title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle(.editItem)
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
ToolbarItemGroup(placement: .topBarLeading) {
|
||||||
|
Button(.cancel, role: .cancel) {
|
||||||
|
model.onCancel(model)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolbarItemGroup(placement: .topBarTrailing) {
|
||||||
|
Button(.done, role: .destructive) {
|
||||||
|
model.onDone(model)
|
||||||
|
}
|
||||||
|
.bold()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ class MainViewModel
|
|||||||
var nowPlayingViewModel = NowPlayingViewModel()
|
var nowPlayingViewModel = NowPlayingViewModel()
|
||||||
var addMediaViewModel = AddMediaView.ViewModel()
|
var addMediaViewModel = AddMediaView.ViewModel()
|
||||||
var serverSelectionViewModel = ServerSelectionToolbarModifier.ViewModel()
|
var serverSelectionViewModel = ServerSelectionToolbarModifier.ViewModel()
|
||||||
|
var editMediaViewModel = EditItemViewModel()
|
||||||
|
|
||||||
private var refreshingFromAPIDepth: UInt8 = 0
|
private var refreshingFromAPIDepth: UInt8 = 0
|
||||||
private var isRefreshingFromAPI: Bool { refreshingFromAPIDepth > 0 }
|
private var isRefreshingFromAPI: Bool { refreshingFromAPIDepth > 0 }
|
||||||
@@ -102,13 +103,26 @@ class MainViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
favoritesModel.onEdit = { [weak self] item in
|
favoritesModel.onEdit = { [weak self] item in
|
||||||
self?.isEditSheetPresented = true
|
guard let self else { return }
|
||||||
|
editMediaViewModel.mediaURL = item.filename
|
||||||
|
editMediaViewModel.title = item.title
|
||||||
|
isEditSheetPresented = true
|
||||||
}
|
}
|
||||||
|
|
||||||
favoritesModel.onQueue = apiCallback { item, api in
|
favoritesModel.onQueue = apiCallback { item, api in
|
||||||
try await api.add(mediaURL: item.filename)
|
try await api.add(mediaURL: item.filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Edit
|
||||||
|
editMediaViewModel.onCancel = { [weak self] _ in
|
||||||
|
self?.isEditSheetPresented = false
|
||||||
|
}
|
||||||
|
|
||||||
|
editMediaViewModel.onDone = apiCallback { [weak self] model, api in
|
||||||
|
self?.isEditSheetPresented = false
|
||||||
|
try await api.renameFavorite(mediaURL: model.mediaURL, title: model.title)
|
||||||
|
}
|
||||||
|
|
||||||
// Add Media
|
// Add Media
|
||||||
addMediaViewModel.onAdd = apiCallback { [weak self] mediaURL, api in
|
addMediaViewModel.onAdd = apiCallback { [weak self] mediaURL, api in
|
||||||
guard let self else { return }
|
guard let self else { return }
|
||||||
|
|||||||
Reference in New Issue
Block a user