forked from shadowfacts/Tusker
Fix various issues when dealing with multiple Compose/Drafts screens simultaneously
This commit is contained in:
parent
f004c82302
commit
d84d402271
|
@ -10,6 +10,7 @@ import Combine
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import TuskerComponents
|
import TuskerComponents
|
||||||
import MatchedGeometryPresentation
|
import MatchedGeometryPresentation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
public final class ComposeController: ViewController {
|
public final class ComposeController: ViewController {
|
||||||
public typealias FetchAttachment = (URL) async -> UIImage?
|
public typealias FetchAttachment = (URL) async -> UIImage?
|
||||||
|
@ -54,6 +55,9 @@ public final class ComposeController: ViewController {
|
||||||
@Published public private(set) var didPostSuccessfully = false
|
@Published public private(set) var didPostSuccessfully = false
|
||||||
@Published var hasChangedLanguageSelection = false
|
@Published var hasChangedLanguageSelection = false
|
||||||
|
|
||||||
|
private var isDisappearing = false
|
||||||
|
private var userConfirmedDelete = false
|
||||||
|
|
||||||
var isPosting: Bool {
|
var isPosting: Bool {
|
||||||
poster != nil
|
poster != nil
|
||||||
}
|
}
|
||||||
|
@ -119,6 +123,7 @@ public final class ComposeController: ViewController {
|
||||||
if #available(iOS 16.0, *) {
|
if #available(iOS 16.0, *) {
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(currentInputModeChanged), name: UITextInputMode.currentInputModeDidChangeNotification, object: nil)
|
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 {
|
public var view: some View {
|
||||||
|
@ -129,6 +134,15 @@ public final class ComposeController: ViewController {
|
||||||
.environment(\.composeUIConfig, config)
|
.environment(\.composeUIConfig, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
@objc private func managedObjectsDidChange(_ notification: Foundation.Notification) {
|
||||||
|
if let deleted = notification.userInfo?[NSDeletedObjectsKey] as? Set<NSManagedObject>,
|
||||||
|
deleted.contains(where: { $0.objectID == self.draft.objectID }),
|
||||||
|
!isDisappearing {
|
||||||
|
self.config.dismiss(.cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func canPaste(itemProviders: [NSItemProvider]) -> Bool {
|
public func canPaste(itemProviders: [NSItemProvider]) -> Bool {
|
||||||
guard itemProviders.allSatisfy({ $0.canLoadObject(ofClass: DraftAttachment.self) }) else {
|
guard itemProviders.allSatisfy({ $0.canLoadObject(ofClass: DraftAttachment.self) }) else {
|
||||||
return false
|
return false
|
||||||
|
@ -172,6 +186,7 @@ public final class ComposeController: ViewController {
|
||||||
@MainActor
|
@MainActor
|
||||||
func cancel(deleteDraft: Bool) {
|
func cancel(deleteDraft: Bool) {
|
||||||
deleteDraftOnDisappear = true
|
deleteDraftOnDisappear = true
|
||||||
|
userConfirmedDelete = true
|
||||||
config.dismiss(.cancel)
|
config.dismiss(.cancel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,16 +231,18 @@ public final class ComposeController: ViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectDraft(_ newDraft: Draft) {
|
func selectDraft(_ newDraft: Draft) {
|
||||||
if !self.draft.hasContent {
|
let oldDraft = self.draft
|
||||||
DraftsPersistentContainer.shared.viewContext.delete(self.draft)
|
self.draft = newDraft
|
||||||
|
|
||||||
|
if !oldDraft.hasContent {
|
||||||
|
DraftsPersistentContainer.shared.viewContext.delete(oldDraft)
|
||||||
}
|
}
|
||||||
DraftsPersistentContainer.shared.save()
|
DraftsPersistentContainer.shared.save()
|
||||||
|
|
||||||
self.draft = newDraft
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func onDisappear() {
|
func onDisappear() {
|
||||||
if deleteDraftOnDisappear && (!draft.hasContent || didPostSuccessfully) {
|
isDisappearing = true
|
||||||
|
if deleteDraftOnDisappear && (!draft.hasContent || didPostSuccessfully || userConfirmedDelete) {
|
||||||
DraftsPersistentContainer.shared.viewContext.delete(draft)
|
DraftsPersistentContainer.shared.viewContext.delete(draft)
|
||||||
}
|
}
|
||||||
DraftsPersistentContainer.shared.save()
|
DraftsPersistentContainer.shared.save()
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import TuskerComponents
|
import TuskerComponents
|
||||||
|
import CoreData
|
||||||
|
|
||||||
class DraftsController: ViewController {
|
class DraftsController: ViewController {
|
||||||
|
|
||||||
|
@ -152,12 +153,14 @@ private struct DraftRow: View {
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
Text(draft.lastModified.formatted(.abbreviatedTimeAgo))
|
if let lastModified = draft.lastModified {
|
||||||
|
Text(lastModified.formatted(.abbreviatedTimeAgo))
|
||||||
.font(.body)
|
.font(.body)
|
||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private extension View {
|
private extension View {
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
|
|
|
@ -108,7 +108,7 @@ private struct DismissFocusedAttachmentButtonStyle: ButtonStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AttachmentDescriptionTextViewID: Hashable {
|
struct AttachmentDescriptionTextViewID: Hashable {
|
||||||
let attachmentID: UUID
|
let attachmentID: UUID!
|
||||||
|
|
||||||
init(_ attachment: DraftAttachment) {
|
init(_ attachment: DraftAttachment) {
|
||||||
self.attachmentID = attachment.id
|
self.attachmentID = attachment.id
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class Draft: NSManagedObject, Identifiable {
|
||||||
@NSManaged public var initialText: String
|
@NSManaged public var initialText: String
|
||||||
@NSManaged public var inReplyToID: String?
|
@NSManaged public var inReplyToID: String?
|
||||||
@NSManaged public var language: String? // ISO 639 language code
|
@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 localOnly: Bool
|
||||||
@NSManaged public var text: String
|
@NSManaged public var text: String
|
||||||
@NSManaged private var visibilityStr: String
|
@NSManaged private var visibilityStr: String
|
||||||
|
|
|
@ -26,7 +26,7 @@ public final class DraftAttachment: NSManagedObject, Identifiable {
|
||||||
@NSManaged public var editedAttachmentURL: URL?
|
@NSManaged public var editedAttachmentURL: URL?
|
||||||
@NSManaged public var fileURL: URL?
|
@NSManaged public var fileURL: URL?
|
||||||
@NSManaged internal var fileType: String?
|
@NSManaged internal var fileType: String?
|
||||||
@NSManaged public var id: UUID
|
@NSManaged public var id: UUID!
|
||||||
|
|
||||||
@NSManaged internal var draft: Draft
|
@NSManaged internal var draft: Draft
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,6 @@ struct PollOptionView: View {
|
||||||
self.remove = remove
|
self.remove = remove
|
||||||
}
|
}
|
||||||
|
|
||||||
private var optionIndex: Int {
|
|
||||||
poll.options.index(of: option)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(spacing: 4) {
|
HStack(spacing: 4) {
|
||||||
Checkbox(radiusFraction: poll.multiple ? 0.1 : 0.5, background: controller.parent.config.backgroundColor)
|
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 {
|
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
|
let maxLength = controller.parent.mastodonController.instanceFeatures.maxPollOptionChars
|
||||||
return EmojiTextField(text: $option.text, placeholder: placeholder, maxLength: maxLength)
|
return EmojiTextField(text: $option.text, placeholder: placeholder, maxLength: maxLength)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue