From 6e5e587998ecc42526090066317703e9576e9b96 Mon Sep 17 00:00:00 2001 From: James Magahern Date: Fri, 20 Jun 2025 18:50:06 -0700 Subject: [PATCH] implements editing items in favorites --- QueueCube/Backend/API.swift | 8 +++ QueueCube/Backend/Utilities.swift | 1 + QueueCube/Localizable/Localizable.xcstrings | 40 ++++++++++++-- QueueCube/Localizable/Strings.swift | 5 +- QueueCube/Views/ContentView.swift | 3 +- QueueCube/Views/EditItemView.swift | 60 +++++++++++++++++++++ QueueCube/Views/MainView.swift | 16 +++++- 7 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 QueueCube/Views/EditItemView.swift diff --git a/QueueCube/Backend/API.swift b/QueueCube/Backend/API.swift index 6dd5ebe..bb9822f 100644 --- a/QueueCube/Backend/API.swift +++ b/QueueCube/Backend/API.swift @@ -139,6 +139,14 @@ struct API .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 { try await request() .path("/playlist/\(index)") diff --git a/QueueCube/Backend/Utilities.swift b/QueueCube/Backend/Utilities.swift index dcd2e9a..f4d4d00 100644 --- a/QueueCube/Backend/Utilities.swift +++ b/QueueCube/Backend/Utilities.swift @@ -89,6 +89,7 @@ struct RequestBuilder enum HTTPMethod: String { case get = "GET" + case put = "PUT" case post = "POST" case delete = "DELETE" } diff --git a/QueueCube/Localizable/Localizable.xcstrings b/QueueCube/Localizable/Localizable.xcstrings index 4982176..87be220 100644 --- a/QueueCube/Localizable/Localizable.xcstrings +++ b/QueueCube/Localizable/Localizable.xcstrings @@ -45,7 +45,14 @@ } }, "ADD_TO_QUEUE" : { - + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Add to Queue" + } + } + } }, "CANCEL" : { "localizations" : { @@ -127,6 +134,16 @@ } } }, + "EDIT_ITEM" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Edit Item" + } + } + } + }, "ENTER_MANUALLY" : { "localizations" : { "en" : { @@ -323,8 +340,15 @@ } } }, - "TODO" : { - + "TITLE" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Title" + } + } + } }, "UNABLE_TO_CONNECT" : { "localizations" : { @@ -336,6 +360,16 @@ } } }, + "URL" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "URL" + } + } + } + }, "VALIDATING" : { "localizations" : { "en" : { diff --git a/QueueCube/Localizable/Strings.swift b/QueueCube/Localizable/Strings.swift index df79547..bc73ee7 100644 --- a/QueueCube/Localizable/Strings.swift +++ b/QueueCube/Localizable/Strings.swift @@ -9,7 +9,7 @@ import SwiftUI extension LocalizedStringKey { - static let serverURL = LocalizedStringKey("SERVER_URL") + static let serverURL = LocalizedStringKey("SERVER_URL") static let settings = LocalizedStringKey("SETTINGS") static let settings_ = LocalizedStringKey("SETTINGS_ELLIPSES") static let done = LocalizedStringKey("DONE") @@ -41,6 +41,9 @@ extension LocalizedStringKey static let copyTitle = LocalizedStringKey("COPY_TITLE") static let copyURL = LocalizedStringKey("COPY_URL") static let edit = LocalizedStringKey("EDIT") + static let editItem = LocalizedStringKey("EDIT_ITEM") static let addToQueue = LocalizedStringKey("ADD_TO_QUEUE") static let notPlaying = LocalizedStringKey("NOT_PLAYING") + static let url = LocalizedStringKey("URL") + static let title = LocalizedStringKey("TITLE") } diff --git a/QueueCube/Views/ContentView.swift b/QueueCube/Views/ContentView.swift index cef63bd..08529cd 100644 --- a/QueueCube/Views/ContentView.swift +++ b/QueueCube/Views/ContentView.swift @@ -31,7 +31,8 @@ struct ContentView: View ) } .sheet(isPresented: $model.isEditSheetPresented) { - Text("TODO") + EditItemView(model: $model.editMediaViewModel) + .presentationBackground(.regularMaterial) } } diff --git a/QueueCube/Views/EditItemView.swift b/QueueCube/Views/EditItemView.swift new file mode 100644 index 0000000..8dd1194 --- /dev/null +++ b/QueueCube/Views/EditItemView.swift @@ -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() + } + } + } + } +} diff --git a/QueueCube/Views/MainView.swift b/QueueCube/Views/MainView.swift index f6936fe..9d4c806 100644 --- a/QueueCube/Views/MainView.swift +++ b/QueueCube/Views/MainView.swift @@ -24,6 +24,7 @@ class MainViewModel var nowPlayingViewModel = NowPlayingViewModel() var addMediaViewModel = AddMediaView.ViewModel() var serverSelectionViewModel = ServerSelectionToolbarModifier.ViewModel() + var editMediaViewModel = EditItemViewModel() private var refreshingFromAPIDepth: UInt8 = 0 private var isRefreshingFromAPI: Bool { refreshingFromAPIDepth > 0 } @@ -102,13 +103,26 @@ class MainViewModel } 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 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 addMediaViewModel.onAdd = apiCallback { [weak self] mediaURL, api in guard let self else { return }