From e0e9d4a1857516fc4ad10185af953959e8c09bea Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sun, 15 Dec 2024 21:04:29 -0500 Subject: [PATCH] Remove old compose rewrite code --- .../ComposeUI/Views/AttachmentRowView.swift | 182 ------------------ .../Attachments/AttachmentsSection.swift | 71 ++++++- .../Views/AttachmentsListSection.swift | 182 ------------------ .../Sources/ComposeUI/Views/ComposeView.swift | 15 +- .../ComposeUI/Views/DraftContentEditor.swift | 2 +- .../Sources/ComposeUI/Views/HeaderView.swift | 16 -- 6 files changed, 72 insertions(+), 396 deletions(-) delete mode 100644 Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift delete mode 100644 Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListSection.swift diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift deleted file mode 100644 index e9e8567e..00000000 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentRowView.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// AttachmentRowView.swift -// ComposeUI -// -// Created by Shadowfacts on 8/18/24. -// - -import SwiftUI -import InstanceFeatures -import Vision - -struct AttachmentRowView: View { - @ObservedObject var attachment: DraftAttachment - @State private var isRecognizingText = false - @State private var textRecognitionError: (any Error)? - - private var thumbnailSize: CGFloat { - #if os(visionOS) - 120 - #else - 80 - #endif - } - - var body: some View { - HStack(alignment: .center, spacing: 4) { - thumbnailView - - descriptionView - } - .alertWithData("Text Recognition Failed", data: $textRecognitionError) { _ in - Button("OK") {} - } message: { error in - Text(error.localizedDescription) - } - } - - // TODO: attachments missing descriptions feature - - private var thumbnailView: some View { - AttachmentThumbnailView(attachment: attachment) - .clipShape(RoundedRectangle(cornerRadius: 8)) - .frame(width: thumbnailSize, height: thumbnailSize) - .contextMenu { - EditDrawingButton(attachment: attachment) - RecognizeTextButton(attachment: attachment, isRecognizingText: $isRecognizingText, error: $textRecognitionError) - DeleteButton(attachment: attachment) - } previewIfAvailable: { - // TODO: need to fix flash of preview changing size - AttachmentThumbnailView(attachment: attachment) - } - } - - @ViewBuilder - private var descriptionView: some View { - if isRecognizingText { - ProgressView() - .progressViewStyle(.circular) - } else { - InlineAttachmentDescriptionView(attachment: attachment, minHeight: thumbnailSize) - } - } -} - -private struct EditDrawingButton: View { - @ObservedObject var attachment: DraftAttachment - @Environment(\.composeUIConfig.presentDrawing) private var presentDrawing - - var body: some View { - if attachment.drawingData != nil { - Button(action: editDrawing) { - Label("Edit Drawing", systemImage: "hand.draw") - } - } - } - - private func editDrawing() { - if case .drawing(let drawing) = attachment.data { - presentDrawing?(drawing) { - attachment.drawing = $0 - } - } - } -} - -private struct RecognizeTextButton: View { - @ObservedObject var attachment: DraftAttachment - @Binding var isRecognizingText: Bool - @Binding var error: (any Error)? - @EnvironmentObject private var instanceFeatures: InstanceFeatures - - var body: some View { - if attachment.type == .image { - Button { - Task { - await recognizeText() - } - } label: { - Label("Recognize Text", systemImage: "doc.text.viewfinder") - } - } - } - - private func recognizeText() async { - isRecognizingText = true - defer { isRecognizingText = false } - - do { - let data = try await getAttachmentData() - let observations = try await runRecognizeTextRequest(data: data) - if let observations { - var text = "" - for observation in observations { - let result = observation.topCandidates(1).first! - text.append(result.string) - text.append("\n") - } - self.attachment.attachmentDescription = text - } - } catch let error as NSError where error.domain == VNErrorDomain && error.code == 1 { - // The perform call throws an error with code 1 if the request is cancelled, which we don't want to show an alert for. - return - } catch { - self.error = error - } - } - - private func getAttachmentData() async throws -> Data { - return try await withCheckedThrowingContinuation { continuation in - attachment.getData(features: instanceFeatures) { result in - switch result { - case .success(let (data, _)): - continuation.resume(returning: data) - case .failure(let error): - continuation.resume(throwing: error) - } - } - } - } - - private func runRecognizeTextRequest(data: Data) async throws -> [VNRecognizedTextObservation]? { - return try await withCheckedThrowingContinuation { continuation in - let handler = VNImageRequestHandler(data: data) - let request = VNRecognizeTextRequest { request, error in - if let error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: request.results as? [VNRecognizedTextObservation]) - } - } - request.recognitionLevel = .accurate - request.usesLanguageCorrection = true - DispatchQueue.global(qos: .userInitiated).async { - try? handler.perform([request]) - } - } - } -} - -private struct DeleteButton: View { - let attachment: DraftAttachment - - var body: some View { - Button(role: .destructive, action: removeAttachment) { - Label("Delete", systemImage: "trash") - } - } - - private func removeAttachment() { - let draft = attachment.draft - var array = draft.draftAttachments - guard let index = array.firstIndex(of: attachment) else { - return - } - array.remove(at: index) - draft.attachments = NSMutableOrderedSet(array: array) - } -} - -//#Preview { -// AttachmentRowView() -//} diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift index f3683a53..d550965f 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/Attachments/AttachmentsSection.swift @@ -9,6 +9,7 @@ import SwiftUI import PhotosUI import PencilKit import GalleryVC +import InstanceFeatures struct AttachmentsSection: View { @ObservedObject var draft: Draft @@ -24,6 +25,19 @@ struct AttachmentsSection: View { // Add 4 to the minItemSize because otherwise drag-and-drop while reordering can alter the contentOffset by that much. .frame(minHeight: 104) } + + static func insertAttachments(in draft: Draft, at index: Int, itemProviders: [NSItemProvider]) { + for provider in itemProviders where provider.canLoadObject(ofClass: DraftAttachment.self) { + provider.loadObject(ofClass: DraftAttachment.self) { object, error in + guard let attachment = object as? DraftAttachment else { return } + DispatchQueue.main.async { + DraftsPersistentContainer.shared.viewContext.insert(attachment) + attachment.draft = draft + draft.attachments.add(attachment) + } + } + } + } } // Use a UIViewControllerRepresentable so we have something from which to present the gallery VC. @@ -277,7 +291,7 @@ private struct AddAttachmentButton: View { Button("Add photo or video", systemImage: "photo") { presentAssetPicker { let draft = viewController.draft! - AttachmentsListSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: $0.map(\.itemProvider)) + AttachmentsSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: $0.map(\.itemProvider)) } } } @@ -308,3 +322,58 @@ private struct AddAttachmentButton: View { .disabled(!enabled) } } + +struct AddAttachmentConditionsModifier: ViewModifier { + @ObservedObject var draft: Draft + @EnvironmentObject private var instanceFeatures: InstanceFeatures + + private var canAddAttachment: Bool { + if instanceFeatures.mastodonAttachmentRestrictions { + return draft.attachments.count < 4 + && draft.draftAttachments.allSatisfy { $0.type == .image } + && draft.poll == nil + } else { + return true + } + } + + func body(content: Content) -> some View { + content + .environment(\.canAddAttachment, canAddAttachment) + } +} + +private struct CanAddAttachmentKey: EnvironmentKey { + static let defaultValue = false +} + +extension EnvironmentValues { + var canAddAttachment: Bool { + get { self[CanAddAttachmentKey.self] } + set { self[CanAddAttachmentKey.self] = newValue } + } +} + +struct DropAttachmentModifier: ViewModifier { + let draft: Draft + @Environment(\.canAddAttachment) private var canAddAttachment + + func body(content: Content) -> some View { + content + .onDrop(of: DraftAttachment.readableTypeIdentifiersForItemProvider, delegate: AttachmentDropDelegate(draft: draft, canAddAttachment: canAddAttachment)) + } +} + +private struct AttachmentDropDelegate: DropDelegate { + let draft: Draft + let canAddAttachment: Bool + + func validateDrop(info: DropInfo) -> Bool { + canAddAttachment + } + + func performDrop(info: DropInfo) -> Bool { + AttachmentsSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: info.itemProviders(for: DraftAttachment.readableTypeIdentifiersForItemProvider)) + return true + } +} diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListSection.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListSection.swift deleted file mode 100644 index 187f38c9..00000000 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/AttachmentsListSection.swift +++ /dev/null @@ -1,182 +0,0 @@ -// -// AttachmentsListSection.swift -// ComposeUI -// -// Created by Shadowfacts on 10/14/24. -// - -import SwiftUI -import InstanceFeatures -import PencilKit - -struct AttachmentsListSection: View { - @ObservedObject var draft: Draft - @ObservedObject var instanceFeatures: InstanceFeatures - @Environment(\.canAddAttachment) private var canAddAttachment - - var body: some View { - attachmentRows - - buttons - .foregroundStyle(.tint) - #if os(visionOS) - .buttonStyle(.bordered) - .labelStyle(AttachmentButtonLabelStyle()) - #endif - } - - private var attachmentRows: some View { - ForEach(draft.draftAttachments) { attachment in - AttachmentRowView(attachment: attachment) - } - .onMove(perform: moveAttachments) - .onDelete(perform: deleteAttachments) - .onInsert(of: DraftAttachment.readableTypeIdentifiersForItemProvider) { offset, providers in - Self.insertAttachments(in: draft, at: offset, itemProviders: providers) - } - } - - @ViewBuilder - private var buttons: some View { - AddPhotoButton(addAttachments: self.addAttachments) - - AddDrawingButton(draft: draft) - - TogglePollButton(draft: draft) - } - - static func insertAttachments(in draft: Draft, at index: Int, itemProviders: [NSItemProvider]) { - for provider in itemProviders where provider.canLoadObject(ofClass: DraftAttachment.self) { - provider.loadObject(ofClass: DraftAttachment.self) { object, error in - guard let attachment = object as? DraftAttachment else { return } - DispatchQueue.main.async { - DraftsPersistentContainer.shared.viewContext.insert(attachment) - attachment.draft = draft - draft.attachments.add(attachment) - } - } - } - } - - private func addAttachments(itemProviders: [NSItemProvider]) { - Self.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: itemProviders) - } - - private func moveAttachments(from source: IndexSet, to destination: Int) { - // just using moveObjects(at:to:) on the draft.attachments NSMutableOrderedSet - // results in the order switching back to the previous order and then to the correct one - // on the subsequent 2 view updates. creating a new set with the proper order doesn't have that problem - var array = draft.draftAttachments - array.move(fromOffsets: source, toOffset: destination) - draft.attachments = NSMutableOrderedSet(array: array) - } - - private func deleteAttachments(at indices: IndexSet) { - var array = draft.draftAttachments - array.remove(atOffsets: indices) - draft.attachments = NSMutableOrderedSet(array: array) - } - -} - -private struct AddPhotoButton: View { - let addAttachments: ([NSItemProvider]) -> Void - @Environment(\.composeUIConfig.presentAssetPicker) private var presentAssetPicker - - var body: some View { - if let presentAssetPicker { - Button("Add photo or video", systemImage: "photo") { - presentAssetPicker { results in - addAttachments(results.map(\.itemProvider)) - } - } - } - } -} - -private struct AddDrawingButton: View { - let draft: Draft - @Environment(\.composeUIConfig.presentDrawing) private var presentDrawing - - var body: some View { - if let presentDrawing { - Button("Add drawing", systemImage: "hand.draw") { - presentDrawing(PKDrawing()) { drawing in - let attachment = DraftAttachment(context: DraftsPersistentContainer.shared.viewContext) - attachment.id = UUID() - attachment.drawing = drawing - attachment.draft = self.draft - self.draft.attachments.add(attachment) - } - } - } - } -} - -private struct TogglePollButton: View { - @ObservedObject var draft: Draft - - var body: some View { - Button(draft.poll == nil ? "Add a poll" : "Remove poll", systemImage: "chart.bar.doc.horizontal") { - withAnimation { - draft.poll = draft.poll == nil ? Poll(context: DraftsPersistentContainer.shared.viewContext) : nil - } - } - .disabled(draft.attachments.count > 0) - } -} - -struct AddAttachmentConditionsModifier: ViewModifier { - @ObservedObject var draft: Draft - @EnvironmentObject private var instanceFeatures: InstanceFeatures - - private var canAddAttachment: Bool { - if instanceFeatures.mastodonAttachmentRestrictions { - return draft.attachments.count < 4 - && draft.draftAttachments.allSatisfy { $0.type == .image } - && draft.poll == nil - } else { - return true - } - } - - func body(content: Content) -> some View { - content - .environment(\.canAddAttachment, canAddAttachment) - } -} - -private struct CanAddAttachmentKey: EnvironmentKey { - static let defaultValue = false -} - -extension EnvironmentValues { - var canAddAttachment: Bool { - get { self[CanAddAttachmentKey.self] } - set { self[CanAddAttachmentKey.self] = newValue } - } -} - -struct DropAttachmentModifier: ViewModifier { - let draft: Draft - @Environment(\.canAddAttachment) private var canAddAttachment - - func body(content: Content) -> some View { - content - .onDrop(of: DraftAttachment.readableTypeIdentifiersForItemProvider, delegate: AttachmentDropDelegate(draft: draft, canAddAttachment: canAddAttachment)) - } -} - -private struct AttachmentDropDelegate: DropDelegate { - let draft: Draft - let canAddAttachment: Bool - - func validateDrop(info: DropInfo) -> Bool { - canAddAttachment - } - - func performDrop(info: DropInfo) -> Bool { - AttachmentsListSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: info.itemProviders(for: DraftAttachment.readableTypeIdentifiersForItemProvider)) - return true - } -} diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift index e6cec01b..e761ecc5 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/ComposeView.swift @@ -92,23 +92,10 @@ struct ComposeView: View { ComposeDraftView(draft: draft, focusedField: $focusedField) } .padding(8) -// NewHeaderView(draft: draft, instanceFeatures: mastodonController.instanceFeatures) -// .listRowInsets(EdgeInsets(top: 8, leading: 8, bottom: 4, trailing: 8)) -// .listRowSeparator(.hidden) -// -// ContentWarningTextField(draft: draft, focusedField: $focusedField) -// .listRowInsets(EdgeInsets(top: 8, leading: 8, bottom: 4, trailing: 8)) -// .listRowSeparator(.hidden) -// -// NewMainTextView(value: $draft.text, focusedField: $focusedField, handleAttachmentDrop: self.addAttachments) -// .listRowInsets(EdgeInsets(top: 8, leading: 8, bottom: 4, trailing: 8)) -// .listRowSeparator(.hidden) -// -// AttachmentsListSection(draft: draft, instanceFeatures: mastodonController.instanceFeatures) } private func addAttachments(_ itemProviders: [NSItemProvider]) { - AttachmentsListSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: itemProviders) + AttachmentsSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: itemProviders) } } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift index 53d3674a..0777cf00 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/DraftContentEditor.swift @@ -33,7 +33,7 @@ struct DraftContentEditor: View { } private func addAttachments(_ providers: [NSItemProvider]) { - AttachmentsListSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: providers) + AttachmentsSection.insertAttachments(in: draft, at: draft.attachments.count, itemProviders: providers) } } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/HeaderView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/HeaderView.swift index 628ae9e4..3c57890a 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/HeaderView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/HeaderView.swift @@ -29,19 +29,3 @@ struct HeaderView: View { }.frame(height: 50) } } - -struct NewHeaderView: View { - @ObservedObject var draft: Draft - @ObservedObject var instanceFeatures: InstanceFeatures - @Environment(\.currentAccount) private var currentAccount - - private var charactersRemaining: Int { - let limit = instanceFeatures.maxStatusChars - let cwCount = draft.contentWarningEnabled ? draft.contentWarning.count : 0 - return limit - (cwCount + CharacterCounter.count(text: draft.text, for: instanceFeatures)) - } - - var body: some View { - HeaderView(currentAccount: currentAccount, charsRemaining: charactersRemaining) - } -}