2023-04-16 13:23:13 -04:00
//
// D r a f t s C o n t r o l l e r . s w i f t
// C o m p o s e U I
//
// C r e a t e d b y S h a d o w f a c t s o n 3 / 7 / 2 3 .
//
import SwiftUI
import TuskerComponents
2023-05-15 22:57:07 -04:00
import CoreData
2023-04-16 13:23:13 -04:00
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 ) {
2023-04-22 21:16:30 -04:00
DraftsPersistentContainer . shared . viewContext . delete ( draft )
2023-04-16 13:23:13 -04:00
}
func closeDrafts ( ) {
isPresented = false
2023-04-22 21:16:30 -04:00
DraftsPersistentContainer . shared . save ( )
2023-04-16 13:23:13 -04:00
}
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
2023-04-22 21:16:30 -04:00
@ FetchRequest ( sortDescriptors : [ SortDescriptor ( \ Draft . lastModified , order : . reverse ) ] ) private var drafts : FetchedResults < Draft >
2023-04-16 13:23:13 -04:00
var body : some View {
NavigationView {
List {
2023-04-22 21:16:30 -04:00
ForEach ( drafts ) { draft in
2023-04-16 13:23:13 -04:00
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
2023-04-22 21:16:30 -04:00
indices . map { drafts [ $0 ] } . forEach ( controller . deleteDraft )
2023-04-16 13:23:13 -04:00
}
}
. 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? " )
}
2023-04-22 21:16:30 -04:00
. onAppear {
drafts . nsPredicate = NSPredicate ( format : " accountID == %@ AND id != %@ AND lastModified != nil " , controller . parent . mastodonController . accountInfo ! . id , currentDraft . id as NSUUID )
}
2023-04-16 13:23:13 -04:00
}
private var cancelButton : some View {
Button ( action : controller . closeDrafts ) {
Text ( " Cancel " )
}
}
}
}
private struct DraftRow : View {
@ ObservedObject var draft : Draft
2023-05-11 09:59:57 -04:00
@ EnvironmentObject private var controller : DraftsController
2023-04-16 13:23:13 -04:00
var body : some View {
HStack {
VStack ( alignment : . leading ) {
2023-05-11 09:59:57 -04:00
if draft . editedStatusID != nil {
// s h o u l d n ' t h a p p e n u n l e s s t h e a p p c r a s h e d / w a s k i l l e d d u r i n g a n e d i t
Text ( " Edit " )
. font ( . body . bold ( ) )
. foregroundColor ( . orange )
}
2023-04-16 13:23:13 -04:00
if draft . contentWarningEnabled {
Text ( draft . contentWarning )
. font ( . body . bold ( ) )
. foregroundColor ( . secondary )
}
Text ( draft . text )
. font ( . body )
HStack ( spacing : 8 ) {
2023-04-22 21:16:30 -04:00
ForEach ( draft . draftAttachments ) { attachment in
2023-05-11 09:59:57 -04:00
ControllerView ( controller : { AttachmentThumbnailController ( attachment : attachment , parent : controller . parent ) } )
2023-04-29 18:49:02 -04:00
. aspectRatio ( contentMode : . fit )
. clipShape ( RoundedRectangle ( cornerRadius : 5 ) )
. frame ( height : 50 )
2023-04-16 13:23:13 -04:00
}
}
}
Spacer ( )
2023-05-15 22:57:07 -04:00
if let lastModified = draft . lastModified {
Text ( lastModified . formatted ( . abbreviatedTimeAgo ) )
. font ( . body )
. foregroundColor ( . secondary )
}
2023-04-16 13:23:13 -04:00
}
}
}
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
}
}
}