implements editing items in favorites

This commit is contained in:
2025-06-20 18:50:06 -07:00
parent d87d6e038e
commit 6e5e587998
7 changed files with 127 additions and 6 deletions

View File

@@ -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)")

View File

@@ -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"
} }

View File

@@ -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" : {

View File

@@ -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")
} }

View File

@@ -31,7 +31,8 @@ struct ContentView: View
) )
} }
.sheet(isPresented: $model.isEditSheetPresented) { .sheet(isPresented: $model.isEditSheetPresented) {
Text("TODO") EditItemView(model: $model.editMediaViewModel)
.presentationBackground(.regularMaterial)
} }
} }

View 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()
}
}
}
}
}

View File

@@ -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 }