From d84d402271b84eed6caa6bc50578563bdc9b5c3d Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Mon, 15 May 2023 22:57:07 -0400 Subject: [PATCH] Fix various issues when dealing with multiple Compose/Drafts screens simultaneously --- .../Controllers/ComposeController.swift | 27 +++++++++++++++---- .../Controllers/DraftsController.swift | 9 ++++--- .../FocusedAttachmentController.swift | 2 +- .../Sources/ComposeUI/CoreData/Draft.swift | 2 +- .../ComposeUI/CoreData/DraftAttachment.swift | 2 +- .../ComposeUI/Views/PollOptionView.swift | 7 ++--- 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift b/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift index 106da1e4..387273a4 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Controllers/ComposeController.swift @@ -10,6 +10,7 @@ import Combine import Pachyderm import TuskerComponents import MatchedGeometryPresentation +import CoreData public final class ComposeController: ViewController { public typealias FetchAttachment = (URL) async -> UIImage? @@ -54,6 +55,9 @@ public final class ComposeController: ViewController { @Published public private(set) var didPostSuccessfully = false @Published var hasChangedLanguageSelection = false + private var isDisappearing = false + private var userConfirmedDelete = false + var isPosting: Bool { poster != nil } @@ -119,6 +123,7 @@ public final class ComposeController: ViewController { if #available(iOS 16.0, *) { NotificationCenter.default.addObserver(self, selector: #selector(currentInputModeChanged), name: UITextInputMode.currentInputModeDidChangeNotification, object: nil) } + NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: DraftsPersistentContainer.shared.viewContext) } public var view: some View { @@ -129,6 +134,15 @@ public final class ComposeController: ViewController { .environment(\.composeUIConfig, config) } + @MainActor + @objc private func managedObjectsDidChange(_ notification: Foundation.Notification) { + if let deleted = notification.userInfo?[NSDeletedObjectsKey] as? Set, + deleted.contains(where: { $0.objectID == self.draft.objectID }), + !isDisappearing { + self.config.dismiss(.cancel) + } + } + public func canPaste(itemProviders: [NSItemProvider]) -> Bool { guard itemProviders.allSatisfy({ $0.canLoadObject(ofClass: DraftAttachment.self) }) else { return false @@ -172,6 +186,7 @@ public final class ComposeController: ViewController { @MainActor func cancel(deleteDraft: Bool) { deleteDraftOnDisappear = true + userConfirmedDelete = true config.dismiss(.cancel) } @@ -216,16 +231,18 @@ public final class ComposeController: ViewController { } func selectDraft(_ newDraft: Draft) { - if !self.draft.hasContent { - DraftsPersistentContainer.shared.viewContext.delete(self.draft) + let oldDraft = self.draft + self.draft = newDraft + + if !oldDraft.hasContent { + DraftsPersistentContainer.shared.viewContext.delete(oldDraft) } DraftsPersistentContainer.shared.save() - - self.draft = newDraft } func onDisappear() { - if deleteDraftOnDisappear && (!draft.hasContent || didPostSuccessfully) { + isDisappearing = true + if deleteDraftOnDisappear && (!draft.hasContent || didPostSuccessfully || userConfirmedDelete) { DraftsPersistentContainer.shared.viewContext.delete(draft) } DraftsPersistentContainer.shared.save() diff --git a/Packages/ComposeUI/Sources/ComposeUI/Controllers/DraftsController.swift b/Packages/ComposeUI/Sources/ComposeUI/Controllers/DraftsController.swift index 13edd88c..0085e374 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Controllers/DraftsController.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Controllers/DraftsController.swift @@ -7,6 +7,7 @@ import SwiftUI import TuskerComponents +import CoreData class DraftsController: ViewController { @@ -152,9 +153,11 @@ private struct DraftRow: View { Spacer() - Text(draft.lastModified.formatted(.abbreviatedTimeAgo)) - .font(.body) - .foregroundColor(.secondary) + if let lastModified = draft.lastModified { + Text(lastModified.formatted(.abbreviatedTimeAgo)) + .font(.body) + .foregroundColor(.secondary) + } } } } diff --git a/Packages/ComposeUI/Sources/ComposeUI/Controllers/FocusedAttachmentController.swift b/Packages/ComposeUI/Sources/ComposeUI/Controllers/FocusedAttachmentController.swift index 201e369b..436724d6 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Controllers/FocusedAttachmentController.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Controllers/FocusedAttachmentController.swift @@ -108,7 +108,7 @@ private struct DismissFocusedAttachmentButtonStyle: ButtonStyle { } struct AttachmentDescriptionTextViewID: Hashable { - let attachmentID: UUID + let attachmentID: UUID! init(_ attachment: DraftAttachment) { self.attachmentID = attachment.id diff --git a/Packages/ComposeUI/Sources/ComposeUI/CoreData/Draft.swift b/Packages/ComposeUI/Sources/ComposeUI/CoreData/Draft.swift index 37923d02..cbbd0fc0 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/CoreData/Draft.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/CoreData/Draft.swift @@ -28,7 +28,7 @@ public class Draft: NSManagedObject, Identifiable { @NSManaged public var initialText: String @NSManaged public var inReplyToID: String? @NSManaged public var language: String? // ISO 639 language code - @NSManaged public var lastModified: Date + @NSManaged public var lastModified: Date! @NSManaged public var localOnly: Bool @NSManaged public var text: String @NSManaged private var visibilityStr: String diff --git a/Packages/ComposeUI/Sources/ComposeUI/CoreData/DraftAttachment.swift b/Packages/ComposeUI/Sources/ComposeUI/CoreData/DraftAttachment.swift index a45057e2..7be3c945 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/CoreData/DraftAttachment.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/CoreData/DraftAttachment.swift @@ -26,7 +26,7 @@ public final class DraftAttachment: NSManagedObject, Identifiable { @NSManaged public var editedAttachmentURL: URL? @NSManaged public var fileURL: URL? @NSManaged internal var fileType: String? - @NSManaged public var id: UUID + @NSManaged public var id: UUID! @NSManaged internal var draft: Draft diff --git a/Packages/ComposeUI/Sources/ComposeUI/Views/PollOptionView.swift b/Packages/ComposeUI/Sources/ComposeUI/Views/PollOptionView.swift index 23e66346..08b86015 100644 --- a/Packages/ComposeUI/Sources/ComposeUI/Views/PollOptionView.swift +++ b/Packages/ComposeUI/Sources/ComposeUI/Views/PollOptionView.swift @@ -18,10 +18,6 @@ struct PollOptionView: View { self.remove = remove } - private var optionIndex: Int { - poll.options.index(of: option) - } - var body: some View { HStack(spacing: 4) { Checkbox(radiusFraction: poll.multiple ? 0.1 : 0.5, background: controller.parent.config.backgroundColor) @@ -41,7 +37,8 @@ struct PollOptionView: View { } private var textField: some View { - let placeholder = "Option \(optionIndex + 1)" + let index = poll.options.index(of: option) + let placeholder = index != NSNotFound ? "Option \(index + 1)" : "" let maxLength = controller.parent.mastodonController.instanceFeatures.maxPollOptionChars return EmojiTextField(text: $option.text, placeholder: placeholder, maxLength: maxLength) }