mirror of
https://github.com/Dimillian/IceCubesApp.git
synced 2024-11-26 10:11:00 +00:00
Editor: Add image ALT
This commit is contained in:
parent
64c57a241a
commit
7ce3446030
5 changed files with 135 additions and 13 deletions
|
@ -2,18 +2,26 @@ import Foundation
|
||||||
|
|
||||||
public enum Media: Endpoint {
|
public enum Media: Endpoint {
|
||||||
case medias
|
case medias
|
||||||
case media(id: String)
|
case media(id: String, description: String?)
|
||||||
|
|
||||||
public func path() -> String {
|
public func path() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .medias:
|
case .medias:
|
||||||
return "media"
|
return "media"
|
||||||
case let .media(id):
|
case let .media(id, _):
|
||||||
return "media/\(id)"
|
return "media/\(id)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func queryItems() -> [URLQueryItem]? {
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import NukeUI
|
||||||
|
|
||||||
struct StatusEditorMediaView: View {
|
struct StatusEditorMediaView: View {
|
||||||
@ObservedObject var viewModel: StatusEditorViewModel
|
@ObservedObject var viewModel: StatusEditorViewModel
|
||||||
|
@State private var editingContainer: StatusEditorViewModel.ImageContainer?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView(.horizontal, showsIndicators: false) {
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
|
@ -14,22 +15,24 @@ struct StatusEditorMediaView: View {
|
||||||
if container.image != nil {
|
if container.image != nil {
|
||||||
makeLocalImage(container: container)
|
makeLocalImage(container: container)
|
||||||
} else if let url = container.mediaAttachement?.url {
|
} else if let url = container.mediaAttachement?.url {
|
||||||
ZStack(alignment: .topTrailing) {
|
Menu {
|
||||||
makeLazyImage(url: url)
|
makeImageMenu(container: container)
|
||||||
Button {
|
} label: {
|
||||||
withAnimation {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
viewModel.mediasImages.removeAll(where: { $0.id == container.id })
|
makeLazyImage(url: url)
|
||||||
|
if container.mediaAttachement?.description?.isEmpty == false {
|
||||||
|
altMarker
|
||||||
}
|
}
|
||||||
} label: {
|
|
||||||
Image(systemName: "xmark.circle")
|
|
||||||
}
|
}
|
||||||
.padding(8)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, .layoutPadding)
|
.padding(.horizontal, .layoutPadding)
|
||||||
}
|
}
|
||||||
|
.sheet(item: $editingContainer) { container in
|
||||||
|
StatusEditorMediaEditView(viewModel: viewModel, container: container)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeLocalImage(container: StatusEditorViewModel.ImageContainer) -> some View {
|
private func makeLocalImage(container: StatusEditorViewModel.ImageContainer) -> some View {
|
||||||
|
@ -84,5 +87,33 @@ struct StatusEditorMediaView: View {
|
||||||
.frame(width: 150, height: 150)
|
.frame(width: 150, height: 150)
|
||||||
.cornerRadius(8)
|
.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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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? {
|
private func uploadMedia(data: Data) async throws -> MediaAttachement? {
|
||||||
guard let client else { return nil }
|
guard let client else { return nil }
|
||||||
|
|
|
@ -20,7 +20,7 @@ For contributors and myself, here is a todo list of features that could be added
|
||||||
- [ ] DM / Conversations
|
- [ ] DM / Conversations
|
||||||
- [X] Lists support
|
- [X] Lists support
|
||||||
- [X] Display images alt
|
- [X] Display images alt
|
||||||
- [ ] Editor: Post image alts
|
- [X] Editor: Post image alts
|
||||||
- [ ] Editor: Add / Edit polls
|
- [ ] Editor: Add / Edit polls
|
||||||
- [ ] Editor: Support video types
|
- [ ] Editor: Support video types
|
||||||
- [ ] Editor: Add photos from camera
|
- [ ] Editor: Add photos from camera
|
||||||
|
|
Loading…
Reference in a new issue