2018-10-12 01:20:58 +00:00
//
// P r e v i e w V i e w C o n t r o l l e r P r o v i d e r . s w i f t
// T u s k e r
//
// C r e a t e d b y S h a d o w f a c t s o n 1 0 / 1 0 / 1 8 .
// C o p y r i g h t © 2 0 1 8 S h a d o w f a c t s . A l l r i g h t s r e s e r v e d .
//
import UIKit
2018-10-20 19:35:56 +00:00
import SafariServices
2019-06-04 21:04:37 +00:00
import Pachyderm
2018-10-12 01:20:58 +00:00
2021-05-22 17:42:53 +00:00
protocol MenuPreviewProvider : AnyObject {
2018-10-12 01:20:58 +00:00
2020-06-27 04:22:14 +00:00
typealias PreviewProviders = ( content : UIContextMenuContentPreviewProvider , actions : ( ) -> [ UIMenuElement ] )
2019-06-04 21:04:37 +00:00
2019-12-14 18:36:05 +00:00
var navigationDelegate : TuskerNavigationDelegate ? { get }
2019-06-04 21:04:37 +00:00
func getPreviewProviders ( for location : CGPoint , sourceViewController : UIViewController ) -> PreviewProviders ?
2018-10-12 01:20:58 +00:00
}
2020-03-21 02:13:04 +00:00
protocol CustomPreviewPresenting {
func presentFromPreview ( presenter : UIViewController )
}
2019-06-04 21:04:37 +00:00
extension MenuPreviewProvider {
2020-01-06 00:54:28 +00:00
private var mastodonController : MastodonController ? { navigationDelegate ? . apiController }
2020-06-27 04:22:14 +00:00
// D e f a u l t n o - o p i m p l e m e n t a t i o n
func getPreviewProviders ( for location : CGPoint , sourceViewController : UIViewController ) -> PreviewProviders ? {
return nil
}
func actionsForProfile ( accountID : String , sourceView : UIView ? ) -> [ UIMenuElement ] {
2020-01-06 00:54:28 +00:00
guard let mastodonController = mastodonController ,
2020-05-02 23:52:35 +00:00
let account = mastodonController . persistentContainer . account ( for : accountID ) else { return [ ] }
2020-06-27 04:22:14 +00:00
2021-06-10 14:52:27 +00:00
guard let loggedInAccountID = mastodonController . accountInfo ? . id else {
// u s e r i s l o g g e d o u t
2020-09-13 19:51:06 +00:00
return [
openInSafariAction ( url : account . url ) ,
2021-06-09 21:09:45 +00:00
createAction ( identifier : " share " , title : " Share " , systemImageName : " square.and.arrow.up " , handler : { [ weak self , weak sourceView ] ( _ ) in
2020-09-13 19:51:06 +00:00
guard let self = self else { return }
self . navigationDelegate ? . showMoreOptions ( forAccount : accountID , sourceView : sourceView )
} )
]
}
2020-06-27 04:22:14 +00:00
var actionsSection : [ UIMenuElement ] = [
2020-07-08 03:23:39 +00:00
createAction ( identifier : " sendmessage " , title : " Send Message " , systemImageName : " envelope " , handler : { [ weak self ] ( _ ) in
guard let self = self else { return }
2020-08-31 23:28:50 +00:00
self . navigationDelegate ? . compose ( mentioningAcct : account . acct )
2019-07-27 22:53:07 +00:00
} ) ,
2020-06-27 04:22:14 +00:00
]
2021-02-06 18:47:45 +00:00
if accountID != mastodonController . account . id {
2020-06-27 04:22:14 +00:00
actionsSection . append ( UIDeferredMenuElement ( { ( elementHandler ) in
guard let mastodonController = self . mastodonController else {
elementHandler ( [ ] )
return
}
let request = Client . getRelationships ( accounts : [ account . id ] )
2021-02-06 18:47:45 +00:00
// t a l k a b o u t c a l l b a c k h e l l : /
2020-07-08 03:23:39 +00:00
mastodonController . run ( request ) { [ weak self ] ( response ) in
2021-06-10 14:36:02 +00:00
guard let self = self ,
case let . success ( results , _ ) = response ,
let relationship = results . first else {
2021-08-12 23:41:00 +00:00
DispatchQueue . main . async {
elementHandler ( [ ] )
}
2021-06-10 14:36:02 +00:00
return
}
let following = relationship . following
DispatchQueue . main . async {
let action = self . createAction ( identifier : " follow " , title : following ? " Unfollow " : " Follow " , systemImageName : following ? " person.badge.minus " : " person.badge.plus " , handler : { ( _ ) in
let request = ( following ? Account . unfollow : Account . follow ) ( accountID )
mastodonController . run ( request ) { ( response ) in
switch response {
case . failure ( _ ) :
fatalError ( )
case let . success ( relationship , _ ) :
mastodonController . persistentContainer . addOrUpdate ( relationship : relationship )
}
}
} )
elementHandler ( [
action
] )
2020-06-27 04:22:14 +00:00
}
}
} ) )
}
2021-06-10 14:52:27 +00:00
var shareSection = [
2020-06-27 04:22:14 +00:00
openInSafariAction ( url : account . url ) ,
2021-06-09 21:09:45 +00:00
createAction ( identifier : " share " , title : " Share " , systemImageName : " square.and.arrow.up " , handler : { [ weak self , weak sourceView ] ( _ ) in
2020-07-08 03:23:39 +00:00
guard let self = self else { return }
2020-01-18 02:29:53 +00:00
self . navigationDelegate ? . showMoreOptions ( forAccount : accountID , sourceView : sourceView )
2019-07-27 22:53:07 +00:00
} )
2019-06-04 21:04:37 +00:00
]
2020-06-27 04:22:14 +00:00
2021-06-10 14:52:27 +00:00
addOpenInNewWindow ( actions : & shareSection , activity : UserActivityManager . showProfileActivity ( id : accountID , accountID : loggedInAccountID ) )
2020-06-27 04:22:14 +00:00
return [
2020-08-15 21:19:45 +00:00
UIMenu ( title : " " , image : nil , identifier : nil , options : [ . displayInline ] , children : shareSection ) ,
2020-06-27 04:22:14 +00:00
UIMenu ( title : " " , image : nil , identifier : nil , options : [ . displayInline ] , children : actionsSection ) ,
]
2018-10-12 01:20:58 +00:00
}
2020-01-18 02:29:53 +00:00
func actionsForURL ( _ url : URL , sourceView : UIView ? ) -> [ UIAction ] {
2019-06-04 21:04:37 +00:00
return [
2020-06-27 04:22:14 +00:00
openInSafariAction ( url : url ) ,
2021-06-09 21:09:45 +00:00
createAction ( identifier : " share " , title : " Share " , systemImageName : " square.and.arrow.up " , handler : { [ weak self , weak sourceView ] ( _ ) in
2020-07-08 03:23:39 +00:00
guard let self = self else { return }
2020-01-18 02:29:53 +00:00
self . navigationDelegate ? . showMoreOptions ( forURL : url , sourceView : sourceView )
2019-07-27 22:53:07 +00:00
} )
2019-06-04 21:04:37 +00:00
]
2018-10-12 01:20:58 +00:00
}
2019-06-04 21:04:37 +00:00
2020-06-27 04:22:14 +00:00
func actionsForHashtag ( _ hashtag : Hashtag , sourceView : UIView ? ) -> [ UIMenuElement ] {
let account = mastodonController ! . accountInfo !
let saved = SavedDataManager . shared . isSaved ( hashtag : hashtag , for : account )
let actionsSection = [
createAction ( identifier : " save " , title : saved ? " Unsave Hashtag " : " Save Hashtag " , systemImageName : " number " , handler : { ( _ ) in
if saved {
SavedDataManager . shared . remove ( hashtag : hashtag , for : account )
} else {
SavedDataManager . shared . add ( hashtag : hashtag , for : account )
}
} )
]
let shareSection = actionsForURL ( hashtag . url , sourceView : sourceView )
return [
2020-08-15 21:19:45 +00:00
UIMenu ( title : " " , image : nil , identifier : nil , options : [ . displayInline ] , children : shareSection ) ,
2020-06-27 04:22:14 +00:00
UIMenu ( title : " " , image : nil , identifier : nil , options : [ . displayInline ] , children : actionsSection ) ,
]
2019-06-04 21:04:37 +00:00
}
2021-06-09 21:10:44 +00:00
func actionsForStatus ( _ status : StatusMO , sourceView : UIView ? , includeReply : Bool = true ) -> [ UIMenuElement ] {
2020-12-24 22:13:44 +00:00
guard let mastodonController = mastodonController else { return [ ] }
2020-09-13 19:51:06 +00:00
2021-06-10 14:52:27 +00:00
guard let accountID = mastodonController . accountInfo ? . id else {
// u s e r i s l o g g e d o u t
2020-09-13 19:51:06 +00:00
return [
openInSafariAction ( url : status . url ! ) ,
2021-06-09 21:09:45 +00:00
createAction ( identifier : " share " , title : " Share " , systemImageName : " square.and.arrow.up " , handler : { [ weak self , weak sourceView ] ( _ ) in
2020-09-13 19:51:06 +00:00
guard let self = self else { return }
2020-12-24 22:13:44 +00:00
self . navigationDelegate ? . showMoreOptions ( forStatus : status . id , sourceView : sourceView )
2020-09-13 19:51:06 +00:00
} )
]
}
2020-06-27 04:22:14 +00:00
let bookmarked = status . bookmarked ? ? false
let muted = status . muted
var actionsSection = [
2020-07-08 03:23:39 +00:00
createAction ( identifier : " bookmark " , title : bookmarked ? " Unbookmark " : " Bookmark " , systemImageName : bookmarked ? " bookmark.fill " : " bookmark " , handler : { [ weak self ] ( _ ) in
guard let self = self else { return }
2020-12-24 22:13:44 +00:00
let request = ( bookmarked ? Status . unbookmark : Status . bookmark ) ( status . id )
2020-06-27 04:22:14 +00:00
self . mastodonController ? . run ( request ) { ( response ) in
if case let . success ( status , _ ) = response {
self . mastodonController ? . persistentContainer . addOrUpdate ( status : status , incrementReferenceCount : false )
}
}
2019-07-27 22:53:07 +00:00
} ) ,
2020-07-08 03:23:39 +00:00
createAction ( identifier : " mute " , title : muted ? " Unmute " : " Mute " , systemImageName : muted ? " speaker " : " speaker.slash " , handler : { [ weak self ] ( _ ) in
guard let self = self else { return }
2020-12-24 22:13:44 +00:00
let request = ( muted ? Status . unmuteConversation : Status . muteConversation ) ( status . id )
2020-06-27 04:22:14 +00:00
self . mastodonController ? . run ( request ) { ( response ) in
if case let . success ( status , _ ) = response {
self . mastodonController ? . persistentContainer . addOrUpdate ( status : status , incrementReferenceCount : false )
}
}
} )
]
2021-06-09 21:10:44 +00:00
if includeReply {
actionsSection . insert ( createAction ( identifier : " reply " , title : " Reply " , systemImageName : " arrowshape.turn.up.left " , handler : { [ weak self ] ( _ ) in
guard let self = self else { return }
self . navigationDelegate ? . compose ( inReplyToID : status . id )
} ) , at : 0 )
}
2020-06-27 04:22:14 +00:00
if mastodonController . account != nil && mastodonController . account . id = = status . account . id {
let pinned = status . pinned ? ? false
2021-06-09 21:10:13 +00:00
actionsSection . append ( createAction ( identifier : " pin " , title : pinned ? " Unpin from Profile " : " Pin to Profile " , systemImageName : pinned ? " pin.slash " : " pin " , handler : { [ weak self ] ( _ ) in
2020-07-08 03:23:39 +00:00
guard let self = self else { return }
2020-12-24 22:13:44 +00:00
let request = ( pinned ? Status . unpin : Status . pin ) ( status . id )
2020-07-08 03:23:39 +00:00
self . mastodonController ? . run ( request , completion : { [ weak self ] ( response ) in
guard let self = self else { return }
2020-06-27 04:22:14 +00:00
if case let . success ( status , _ ) = response {
self . mastodonController ? . persistentContainer . addOrUpdate ( status : status , incrementReferenceCount : false )
}
} )
} ) )
}
2021-05-05 21:51:11 +00:00
if status . poll != nil {
actionsSection . insert ( createAction ( identifier : " refresh " , title : " Refresh Poll " , systemImageName : " arrow.clockwise " , handler : { [ weak self ] ( _ ) in
guard let mastodonController = self ? . mastodonController else { return }
let request = Client . getStatus ( id : status . id )
mastodonController . run ( request , completion : { ( response ) in
if case let . success ( status , _ ) = response {
// t o d o : t h i s s h o u l d n ' t r e a l l y u s e t h e v i e w C o n t e x t , b u t f o r s o m e r e a s o n s a v i n g t h e
// b a c k g r o u n d C o n t e x t w i t h t h e n e w v e r s i o n o f t h e s t a t u s i s n ' t u p d a t i n g t h e v i e w C o n t e x t
mastodonController . persistentContainer . addOrUpdate ( status : status , incrementReferenceCount : false , context : mastodonController . persistentContainer . viewContext )
}
} )
} ) , at : 0 )
}
2020-12-14 03:37:37 +00:00
var shareSection = [
2020-06-27 04:22:14 +00:00
openInSafariAction ( url : status . url ! ) ,
2021-06-09 21:09:45 +00:00
createAction ( identifier : " share " , title : " Share " , systemImageName : " square.and.arrow.up " , handler : { [ weak self , weak sourceView ] ( _ ) in
2020-07-08 03:23:39 +00:00
guard let self = self else { return }
2020-12-24 22:13:44 +00:00
self . navigationDelegate ? . showMoreOptions ( forStatus : status . id , sourceView : sourceView )
2020-06-27 04:22:14 +00:00
} ) ,
]
2021-06-10 14:52:27 +00:00
addOpenInNewWindow ( actions : & shareSection , activity : UserActivityManager . showConversationActivity ( mainStatusID : status . id , accountID : accountID ) )
2020-12-14 03:37:37 +00:00
2020-06-27 04:22:14 +00:00
return [
UIMenu ( title : " " , image : nil , identifier : nil , options : [ . displayInline ] , children : shareSection ) ,
2020-08-15 21:19:45 +00:00
UIMenu ( title : " " , image : nil , identifier : nil , options : [ . displayInline ] , children : actionsSection ) ,
2019-06-04 21:04:37 +00:00
]
}
2019-09-06 21:56:45 +00:00
2020-12-14 03:37:37 +00:00
private func createAction ( identifier : String , title : String , systemImageName : String ? , handler : @ escaping UIActionHandler ) -> UIAction {
let image : UIImage ?
if let name = systemImageName {
image = UIImage ( systemName : name )
} else {
image = nil
}
return UIAction ( title : title , image : image , identifier : UIAction . Identifier ( identifier ) , discoverabilityTitle : nil , attributes : [ ] , state : . off , handler : handler )
2019-07-27 22:53:07 +00:00
}
2020-06-27 04:22:14 +00:00
private func openInSafariAction ( url : URL ) -> UIAction {
2021-02-06 20:29:35 +00:00
return createAction ( identifier : " openinsafari " , title : " Open in Safari " , systemImageName : " safari " , handler : { [ weak self ] ( _ ) in
self ? . navigationDelegate ? . selected ( url : url , allowUniversalLinks : false )
2020-06-27 04:22:14 +00:00
} )
}
2021-06-10 14:52:27 +00:00
private func addOpenInNewWindow ( actions : inout [ UIAction ] , activity : @ escaping @autoclosure ( ) -> NSUserActivity ) {
#if SDK_IOS_15
if #available ( iOS 15.0 , * ) {
let options = UIWindowScene . ActivationRequestOptions ( )
options . preferredPresentationStyle = . automatic
actions . append ( UIWindowScene . ActivationAction { ( _ ) in
return . init ( userActivity : activity ( ) , options : options , preview : nil )
} )
} else if UIDevice . current . userInterfaceIdiom = = . pad || UIDevice . current . userInterfaceIdiom = = . mac {
actions . append ( createAction ( identifier : " new_window " , title : " Open in New Window " , systemImageName : " rectangle.badge.plus " , handler : { ( _ ) in
UIApplication . shared . requestSceneSessionActivation ( nil , userActivity : activity ( ) , options : nil , errorHandler : nil )
} ) )
}
#else
if UIDevice . current . userInterfaceIdiom = = . pad || UIDevice . current . userInterfaceIdiom = = . mac {
actions . append ( createAction ( identifier : " new_window " , title : " Open in New Window " , systemImageName : " rectangle.badge.plus " , handler : { ( _ ) in
UIApplication . shared . requestSceneSessionActivation ( nil , userActivity : activity ( ) , options : nil , errorHandler : nil )
} ) )
}
#endif
}
2018-10-12 01:20:58 +00:00
}
2020-03-21 02:13:04 +00:00
extension LargeImageViewController : CustomPreviewPresenting {
func presentFromPreview ( presenter : UIViewController ) {
presenter . present ( self , animated : true )
}
}
extension GalleryViewController : CustomPreviewPresenting {
func presentFromPreview ( presenter : UIViewController ) {
presenter . present ( self , animated : true )
}
}
extension SFSafariViewController : CustomPreviewPresenting {
func presentFromPreview ( presenter : UIViewController ) {
presenter . present ( self , animated : true )
}
}