forked from shadowfacts/Tusker
175 lines
5.8 KiB
Swift
175 lines
5.8 KiB
Swift
//
|
|
// DraftsController.swift
|
|
// ComposeUI
|
|
//
|
|
// Created by Shadowfacts on 3/7/23.
|
|
//
|
|
|
|
import SwiftUI
|
|
import TuskerComponents
|
|
import CoreData
|
|
|
|
class DraftsController: ViewController {
|
|
|
|
unowned let parent: ComposeController
|
|
@Binding var isPresented: Bool
|
|
|
|
@Published var draftForDifferentReply: Draft?
|
|
|
|
init(parent: ComposeController, isPresented: Binding<Bool>) {
|
|
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) {
|
|
DraftsPersistentContainer.shared.viewContext.delete(draft)
|
|
}
|
|
|
|
func closeDrafts() {
|
|
isPresented = false
|
|
DraftsPersistentContainer.shared.save()
|
|
}
|
|
|
|
struct DraftsRepresentable: UIViewControllerRepresentable {
|
|
typealias UIViewControllerType = UIHostingController<DraftsView>
|
|
|
|
func makeUIViewController(context: Context) -> UIHostingController<DraftsController.DraftsView> {
|
|
return UIHostingController(rootView: DraftsView())
|
|
}
|
|
|
|
func updateUIViewController(_ uiViewController: UIHostingController<DraftsController.DraftsView>, context: Context) {
|
|
}
|
|
}
|
|
|
|
struct DraftsView: View {
|
|
@EnvironmentObject private var controller: DraftsController
|
|
@EnvironmentObject private var currentDraft: Draft
|
|
@FetchRequest(sortDescriptors: [SortDescriptor(\Draft.lastModified, order: .reverse)]) private var drafts: FetchedResults<Draft>
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
List {
|
|
ForEach(drafts) { 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 { drafts[$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?")
|
|
}
|
|
.onAppear {
|
|
drafts.nsPredicate = NSPredicate(format: "accountID == %@ AND id != %@ AND lastModified != nil", controller.parent.mastodonController.accountInfo!.id, currentDraft.id as NSUUID)
|
|
}
|
|
}
|
|
|
|
private var cancelButton: some View {
|
|
Button(action: controller.closeDrafts) {
|
|
Text("Cancel")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct DraftRow: View {
|
|
@ObservedObject var draft: Draft
|
|
@EnvironmentObject private var controller: DraftsController
|
|
|
|
var body: some View {
|
|
HStack {
|
|
VStack(alignment: .leading) {
|
|
if draft.editedStatusID != nil {
|
|
// shouldn't happen unless the app crashed/was killed during an edit
|
|
Text("Edit")
|
|
.font(.body.bold())
|
|
.foregroundColor(.orange)
|
|
}
|
|
|
|
if draft.contentWarningEnabled {
|
|
Text(draft.contentWarning)
|
|
.font(.body.bold())
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Text(draft.text)
|
|
.font(.body)
|
|
|
|
HStack(spacing: 8) {
|
|
ForEach(draft.draftAttachments) { attachment in
|
|
ControllerView(controller: { AttachmentThumbnailController(attachment: attachment, parent: controller.parent) })
|
|
.aspectRatio(contentMode: .fit)
|
|
.clipShape(RoundedRectangle(cornerRadius: 5))
|
|
.frame(height: 50)
|
|
}
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
if let lastModified = draft.lastModified {
|
|
Text(lastModified.formatted(.abbreviatedTimeAgo))
|
|
.font(.body)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension View {
|
|
@ViewBuilder
|
|
func ifLet<T, V: View>(_ value: T?, modify: (Self, T) -> V) -> some View {
|
|
if let value {
|
|
modify(self, value)
|
|
} else {
|
|
self
|
|
}
|
|
}
|
|
}
|