// // DraftsController.swift // ComposeUI // // Created by Shadowfacts on 3/7/23. // import SwiftUI import TuskerComponents class DraftsController: ViewController { unowned let parent: ComposeController @Binding var isPresented: Bool @Published var draftForDifferentReply: Draft? init(parent: ComposeController, isPresented: Binding) { self.parent = parent self._isPresented = isPresented } var view: some View { DraftsRepresentable() } func maybeSelectDraft(_ draft: Draft) { if draft.inReplyToID != parent.draft.inReplyToID, parent.draft.hasContent { draftForDifferentReply = draft } else { confirmSelectDraft(draft) } } func cancelSelectingDraft() { draftForDifferentReply = nil } func confirmSelectDraft(_ draft: Draft) { parent.selectDraft(draft) closeDrafts() } func deleteDraft(_ draft: Draft) { DraftsManager.shared.remove(draft) } func closeDrafts() { isPresented = false DraftsManager.save() } struct DraftsRepresentable: UIViewControllerRepresentable { typealias UIViewControllerType = UIHostingController func makeUIViewController(context: Context) -> UIHostingController { return UIHostingController(rootView: DraftsView()) } func updateUIViewController(_ uiViewController: UIHostingController, context: Context) { } } struct DraftsView: View { @EnvironmentObject private var controller: DraftsController @EnvironmentObject private var currentDraft: Draft @ObservedObject private var draftsManager = DraftsManager.shared private var visibleDrafts: [Draft] { draftsManager.sorted.filter { $0.accountID == controller.parent.mastodonController.accountInfo!.id && $0.id != currentDraft.id } } var body: some View { NavigationView { List { ForEach(visibleDrafts) { draft in Button(action: { controller.maybeSelectDraft(draft) }) { DraftRow(draft: draft) } .contextMenu { Button(role: .destructive, action: { controller.deleteDraft(draft) }) { Label("Delete Draft", systemImage: "trash") } } .ifLet(controller.parent.config.userActivityForDraft(draft), modify: { view, activity in view.onDrag { activity } }) } .onDelete { indices in indices.map { visibleDrafts[$0] }.forEach(controller.deleteDraft) } } .listStyle(.plain) .navigationTitle("Drafts") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { cancelButton } } } .alertWithData("Different Reply", data: $controller.draftForDifferentReply) { draft in Button(role: .cancel, action: controller.cancelSelectingDraft) { Text("Cancel") } Button(action: { controller.confirmSelectDraft(draft) }) { Text("Restore Draft") } } message: { _ in Text("The selected draft is a reply to a different post, do you wish to use it?") } } private var cancelButton: some View { Button(action: controller.closeDrafts) { Text("Cancel") } } } } private struct DraftRow: View { @ObservedObject var draft: Draft var body: some View { HStack { VStack(alignment: .leading) { if draft.contentWarningEnabled { Text(draft.contentWarning) .font(.body.bold()) .foregroundColor(.secondary) } Text(draft.text) .font(.body) HStack(spacing: 8) { ForEach(draft.attachments) { attachment in AttachmentThumbnailView(attachment: attachment, fullSize: false) .frame(width: 50, height: 50) .cornerRadius(5) } } } Spacer() Text(draft.lastModified.formatted(.abbreviatedTimeAgo)) .font(.body) .foregroundColor(.secondary) } } } private extension View { @ViewBuilder func ifLet(_ value: T?, modify: (Self, T) -> V) -> some View { if let value { modify(self, value) } else { self } } }