diff --git a/Packages/Network/Sources/Network/Endpoint/Media.swift b/Packages/Network/Sources/Network/Endpoint/Media.swift index de1a960e..086c5f5c 100644 --- a/Packages/Network/Sources/Network/Endpoint/Media.swift +++ b/Packages/Network/Sources/Network/Endpoint/Media.swift @@ -2,18 +2,26 @@ import Foundation public enum Media: Endpoint { case medias - case media(id: String) + case media(id: String, description: String?) public func path() -> String { switch self { case .medias: return "media" - case let .media(id): + case let .media(id, _): return "media/\(id)" } } public func queryItems() -> [URLQueryItem]? { - return nil + switch self { + case let .media(_, description): + if let description { + return [.init(name: "description", value: description)] + } + return nil + default: + return nil + } } } diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaEditView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaEditView.swift new file mode 100644 index 00000000..28fe9ce3 --- /dev/null +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaEditView.swift @@ -0,0 +1,69 @@ +import SwiftUI +import Models +import DesignSystem +import Shimmer + +struct StatusEditorMediaEditView: View { + @Environment(\.dismiss) private var dismiss + @EnvironmentObject private var theme: Theme + @ObservedObject var viewModel: StatusEditorViewModel + let container: StatusEditorViewModel.ImageContainer + + @State private var imageDescription: String = "" + + var body: some View { + NavigationStack { + Form { + Section { + TextField("Image description", text: $imageDescription, axis: .horizontal) + } + .listRowBackground(theme.primaryBackgroundColor) + Section { + if let url = container.mediaAttachement?.url { + AsyncImage( + url: url, + content: { image in + image + .resizable() + .aspectRatio(contentMode: .fill) + .cornerRadius(8) + .padding(8) + }, + placeholder: { + RoundedRectangle(cornerRadius: 8) + .fill(Color.gray) + .frame(height: 200) + .shimmering() + }) + } + } + .listRowBackground(theme.primaryBackgroundColor) + } + .scrollContentBackground(.hidden) + .background(theme.secondaryBackgroundColor) + .onAppear { + imageDescription = container.mediaAttachement?.description ?? "" + } + .navigationTitle("Edit Image") + .navigationBarTitleDisplayMode(.inline) + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button("Done") { + if !imageDescription.isEmpty { + Task { + await viewModel.addDescription(container: container, description: imageDescription) + } + } + dismiss() + } + } + + ToolbarItem(placement: .navigationBarLeading) { + Button("Cancel") { + dismiss() + } + } + } + } + } +} diff --git a/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift b/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift index 0d5fe2a6..5db11c0a 100644 --- a/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift +++ b/Packages/Status/Sources/Status/Editor/Components/StatusEditorMediaView.swift @@ -6,6 +6,7 @@ import NukeUI struct StatusEditorMediaView: View { @ObservedObject var viewModel: StatusEditorViewModel + @State private var editingContainer: StatusEditorViewModel.ImageContainer? var body: some View { ScrollView(.horizontal, showsIndicators: false) { @@ -14,22 +15,24 @@ struct StatusEditorMediaView: View { if container.image != nil { makeLocalImage(container: container) } else if let url = container.mediaAttachement?.url { - ZStack(alignment: .topTrailing) { - makeLazyImage(url: url) - Button { - withAnimation { - viewModel.mediasImages.removeAll(where: { $0.id == container.id }) + Menu { + makeImageMenu(container: container) + } label: { + ZStack(alignment: .bottomTrailing) { + makeLazyImage(url: url) + if container.mediaAttachement?.description?.isEmpty == false { + altMarker } - } label: { - Image(systemName: "xmark.circle") } - .padding(8) } } } } .padding(.horizontal, .layoutPadding) } + .sheet(item: $editingContainer) { container in + StatusEditorMediaEditView(viewModel: viewModel, container: container) + } } private func makeLocalImage(container: StatusEditorViewModel.ImageContainer) -> some View { @@ -84,5 +87,33 @@ struct StatusEditorMediaView: View { .frame(width: 150, height: 150) .cornerRadius(8) } - + + @ViewBuilder + private func makeImageMenu(container: StatusEditorViewModel.ImageContainer) -> some View { + Button { + editingContainer = container + } label: { + Label(editingContainer?.mediaAttachement?.description?.isEmpty == false ? + "Edit description" : "Add description", + systemImage: "pencil.line") + } + Button(role: .destructive) { + withAnimation { + viewModel.mediasImages.removeAll(where: { $0.id == container.id }) + } + } label: { + Label("Delete", systemImage: "trash") + } + } + + private var altMarker: some View { + Button { + } label: { + Text("ALT") + .font(.caption2) + } + .padding(4) + .background(.thinMaterial) + .cornerRadius(8) + } } diff --git a/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift b/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift index 1ad93095..aa7f12c3 100644 --- a/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift +++ b/Packages/Status/Sources/Status/Editor/StatusEditorViewModel.swift @@ -304,6 +304,20 @@ public class StatusEditorViewModel: ObservableObject { } } } + + func addDescription(container: ImageContainer, description: String) async { + guard let client, let attachment = container.mediaAttachement else { return } + if let index = indexOf(container: container) { + let originalContainer = mediasImages[index] + do { + let media: MediaAttachement = try await client.put(endpoint: Media.media(id: attachment.id, + description: description)) + mediasImages[index] = .init(image: nil, mediaAttachement: media, error: nil) + } catch { + + } + } + } private func uploadMedia(data: Data) async throws -> MediaAttachement? { guard let client else { return nil } diff --git a/README.md b/README.md index 45f8cc76..fd4cea5b 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ For contributors and myself, here is a todo list of features that could be added - [ ] DM / Conversations - [X] Lists support - [X] Display images alt -- [ ] Editor: Post image alts +- [X] Editor: Post image alts - [ ] Editor: Add / Edit polls - [ ] Editor: Support video types - [ ] Editor: Add photos from camera