Remove old compose rewrite code
This commit is contained in:
parent
6730575aed
commit
e0e9d4a185
@ -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()
|
||||
//}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user