2019-12-17 05:22:25 +00:00
//
// E x p l o r e V i e w C o n t r o l l 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 2 / 1 4 / 1 9 .
// C o p y r i g h t © 2 0 1 9 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
import Combine
2019-12-17 23:48:29 +00:00
import Pachyderm
2019-12-17 05:22:25 +00:00
class ExploreViewController : EnhancedTableViewController {
2020-05-13 22:57:04 +00:00
weak var mastodonController : MastodonController !
2020-01-05 20:25:07 +00:00
2019-12-18 02:40:08 +00:00
var dataSource : DataSource !
2019-12-17 05:22:25 +00:00
var resultsController : SearchResultsViewController !
var searchController : UISearchController !
2020-01-05 20:25:07 +00:00
init ( mastodonController : MastodonController ) {
self . mastodonController = mastodonController
2019-12-17 05:22:25 +00:00
super . init ( style : . insetGrouped )
title = NSLocalizedString ( " Explore " , comment : " explore tab title " )
tabBarItem . image = UIImage ( systemName : " magnifyingglass " )
}
required init ? ( coder : NSCoder ) {
fatalError ( " init(coder:) has not been implemented " )
}
override func viewDidLoad ( ) {
super . viewDidLoad ( )
tableView . register ( UINib ( nibName : " BasicTableViewCell " , bundle : . main ) , forCellReuseIdentifier : " basicCell " )
dataSource = DataSource ( tableView : tableView , cellProvider : { ( tableView , indexPath , item ) -> UITableViewCell ? in
2019-12-18 02:18:32 +00:00
let cell = tableView . dequeueReusableCell ( withIdentifier : " basicCell " , for : indexPath )
2019-12-17 05:22:25 +00:00
switch item {
case . bookmarks :
cell . imageView ! . image = UIImage ( systemName : " bookmark.fill " )
cell . textLabel ! . text = NSLocalizedString ( " Bookmarks " , comment : " bookmarks nav item title " )
cell . accessoryType = . disclosureIndicator
2019-12-18 02:18:32 +00:00
2019-12-17 23:48:29 +00:00
case let . list ( list ) :
2019-12-20 02:20:29 +00:00
cell . imageView ! . image = UIImage ( systemName : " list.bullet " )
2019-12-17 23:48:29 +00:00
cell . textLabel ! . text = list . title
2019-12-17 05:22:25 +00:00
cell . accessoryType = . disclosureIndicator
2019-12-18 02:18:32 +00:00
case . addList :
cell . imageView ! . image = UIImage ( systemName : " plus " )
cell . textLabel ! . text = NSLocalizedString ( " New List... " , comment : " new list nav item title " )
cell . accessoryType = . none
2019-12-20 02:20:29 +00:00
case let . savedHashtag ( hashtag ) :
cell . imageView ! . image = UIImage ( systemName : " number " )
cell . textLabel ! . text = hashtag . name
cell . accessoryType = . disclosureIndicator
case . addSavedHashtag :
cell . imageView ! . image = UIImage ( systemName : " plus " )
cell . textLabel ! . text = NSLocalizedString ( " Save Hashtag... " , comment : " save hashtag nav item title " )
cell . accessoryType = . none
2019-12-20 03:41:23 +00:00
case let . savedInstance ( url ) :
cell . imageView ! . image = UIImage ( systemName : " globe " )
cell . textLabel ! . text = url . host !
cell . accessoryType = . disclosureIndicator
case . findInstance :
cell . imageView ! . image = UIImage ( systemName : " magnifyingglass " )
cell . textLabel ! . text = NSLocalizedString ( " Find An Instance... " , comment : " find instance nav item title " )
cell . accessoryType = . none
2019-12-17 05:22:25 +00:00
}
2019-12-18 02:18:32 +00:00
return cell
2019-12-17 05:22:25 +00:00
} )
2019-12-18 02:40:08 +00:00
dataSource . exploreController = self
2019-12-17 05:22:25 +00:00
2020-01-20 16:48:47 +00:00
let account = mastodonController . accountInfo !
2019-12-17 05:22:25 +00:00
var snapshot = NSDiffableDataSourceSnapshot < Section , Item > ( )
2019-12-20 03:41:23 +00:00
snapshot . appendSections ( [ . bookmarks , . lists , . savedHashtags , . savedInstances ] )
2019-12-17 05:22:25 +00:00
snapshot . appendItems ( [ . bookmarks ] , toSection : . bookmarks )
2019-12-18 02:18:32 +00:00
snapshot . appendItems ( [ . addList ] , toSection : . lists )
2020-01-20 16:48:47 +00:00
snapshot . appendItems ( SavedDataManager . shared . sortedHashtags ( for : account ) . map { . savedHashtag ( $0 ) } + [ . addSavedHashtag ] , toSection : . savedHashtags )
snapshot . appendItems ( SavedDataManager . shared . savedInstances ( for : account ) . map { . savedInstance ( $0 ) } + [ . findInstance ] , toSection : . savedInstances )
2019-12-17 05:22:25 +00:00
// t h e i n i t i a l , s t a t i c i t e m s s h o u l d n o t b e d i s p l a y e d w i t h a n a n i m a t i o n
UIView . performWithoutAnimation {
dataSource . apply ( snapshot )
}
2020-01-05 20:25:07 +00:00
resultsController = SearchResultsViewController ( mastodonController : mastodonController )
2019-12-17 05:22:25 +00:00
resultsController . exploreNavigationController = self . navigationController !
searchController = UISearchController ( searchResultsController : resultsController )
searchController . searchResultsUpdater = resultsController
searchController . searchBar . autocapitalizationType = . none
searchController . searchBar . delegate = resultsController
definesPresentationContext = true
navigationItem . searchController = searchController
navigationItem . hidesSearchBarWhenScrolling = false
2019-12-17 23:48:29 +00:00
2019-12-20 02:20:29 +00:00
NotificationCenter . default . addObserver ( self , selector : #selector ( savedHashtagsChanged ) , name : . savedHashtagsChanged , object : nil )
2019-12-20 03:41:23 +00:00
NotificationCenter . default . addObserver ( self , selector : #selector ( savedInstancesChanged ) , name : . savedInstancesChanged , object : nil )
2019-12-20 02:20:29 +00:00
2019-12-18 02:18:32 +00:00
reloadLists ( )
}
func reloadLists ( ) {
2020-01-05 19:00:39 +00:00
let request = Client . getLists ( )
2020-01-05 20:25:07 +00:00
mastodonController . run ( request ) { ( response ) in
2019-12-17 23:48:29 +00:00
guard case let . success ( lists , _ ) = response else {
fatalError ( )
}
var snapshot = self . dataSource . snapshot ( )
2019-12-20 02:20:29 +00:00
snapshot . deleteItems ( snapshot . itemIdentifiers ( inSection : . lists ) )
2019-12-18 02:18:32 +00:00
snapshot . appendItems ( lists . map { . list ( $0 ) } + [ . addList ] , toSection : . lists )
2019-12-20 02:20:29 +00:00
2019-12-17 23:48:29 +00:00
DispatchQueue . main . async {
self . dataSource . apply ( snapshot )
}
}
2019-12-17 05:22:25 +00:00
}
2019-12-17 23:48:29 +00:00
2019-12-20 02:20:29 +00:00
@objc func savedHashtagsChanged ( ) {
2020-01-20 16:48:47 +00:00
let account = mastodonController . accountInfo !
2019-12-20 02:20:29 +00:00
var snapshot = dataSource . snapshot ( )
snapshot . deleteItems ( snapshot . itemIdentifiers ( inSection : . savedHashtags ) )
2020-01-20 16:48:47 +00:00
snapshot . appendItems ( SavedDataManager . shared . sortedHashtags ( for : account ) . map { . savedHashtag ( $0 ) } + [ . addSavedHashtag ] , toSection : . savedHashtags )
2019-12-20 02:20:29 +00:00
dataSource . apply ( snapshot )
}
2019-12-20 03:41:23 +00:00
@objc func savedInstancesChanged ( ) {
2020-01-20 16:48:47 +00:00
let account = mastodonController . accountInfo !
2019-12-20 03:41:23 +00:00
var snapshot = dataSource . snapshot ( )
snapshot . deleteItems ( snapshot . itemIdentifiers ( inSection : . savedInstances ) )
2020-01-20 16:48:47 +00:00
snapshot . appendItems ( SavedDataManager . shared . savedInstances ( for : account ) . map { . savedInstance ( $0 ) } + [ . findInstance ] , toSection : . savedInstances )
2019-12-20 03:41:23 +00:00
dataSource . apply ( snapshot )
}
2019-12-20 02:20:29 +00:00
func deleteList ( _ list : List ) {
let title = String ( format : NSLocalizedString ( " Are you sure want to delete the '%@' list? " , comment : " delete list alert title " ) , list . title )
let alert = UIAlertController ( title : title , message : nil , preferredStyle : . alert )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " Cancel " , comment : " delete list alert cancel button " ) , style : . cancel , handler : nil ) )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " Delete List " , comment : " delete list alert confirm button " ) , style : . destructive , handler : { ( _ ) in
let request = List . delete ( list )
2020-01-05 20:25:07 +00:00
self . mastodonController . run ( request ) { ( response ) in
2019-12-20 02:20:29 +00:00
guard case . success ( _ , _ ) = response else {
fatalError ( )
}
var snapshot = self . dataSource . snapshot ( )
snapshot . deleteItems ( [ . list ( list ) ] )
DispatchQueue . main . async {
self . dataSource . apply ( snapshot )
}
}
} ) )
present ( alert , animated : true )
}
func removeSavedHashtag ( _ hashtag : Hashtag ) {
2020-01-20 16:48:47 +00:00
let account = mastodonController . accountInfo !
SavedDataManager . shared . remove ( hashtag : hashtag , for : account )
2019-12-20 02:20:29 +00:00
}
2019-12-20 03:41:23 +00:00
func removeSavedInstance ( _ instanceURL : URL ) {
2020-01-20 16:48:47 +00:00
let account = mastodonController . accountInfo !
SavedDataManager . shared . remove ( instance : instanceURL , for : account )
2019-12-20 03:41:23 +00:00
}
2019-12-17 05:22:25 +00:00
// MARK: - T a b l e v i e w d e l e g a t e
override func tableView ( _ tableView : UITableView , didSelectRowAt indexPath : IndexPath ) {
switch dataSource . itemIdentifier ( for : indexPath ) {
case nil :
return
case . bookmarks :
2020-01-05 20:25:07 +00:00
show ( BookmarksTableViewController ( mastodonController : mastodonController ) , sender : nil )
2019-12-17 05:22:25 +00:00
2019-12-17 23:48:29 +00:00
case let . list ( list ) :
2020-01-05 20:25:07 +00:00
show ( ListTimelineViewController ( for : list , mastodonController : mastodonController ) , sender : nil )
2019-12-18 02:18:32 +00:00
case . addList :
tableView . selectRow ( at : nil , animated : true , scrollPosition : . none )
let alert = UIAlertController ( title : NSLocalizedString ( " New List " , comment : " new list alert title " ) , message : NSLocalizedString ( " Choose a title for your new list " , comment : " new list alert message " ) , preferredStyle : . alert )
alert . addTextField ( configurationHandler : nil )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " Cancel " , comment : " new list alert cancel button " ) , style : . cancel , handler : nil ) )
alert . addAction ( UIAlertAction ( title : NSLocalizedString ( " Create List " , comment : " new list create button " ) , style : . default , handler : { ( _ ) in
guard let title = alert . textFields ? . first ? . text else {
fatalError ( )
}
2020-01-05 19:00:39 +00:00
let request = Client . createList ( title : title )
2020-01-05 20:25:07 +00:00
self . mastodonController . run ( request ) { ( response ) in
2019-12-18 02:18:32 +00:00
guard case let . success ( list , _ ) = response else { fatalError ( ) }
self . reloadLists ( )
DispatchQueue . main . async {
2020-01-05 20:25:07 +00:00
let listTimelineController = ListTimelineViewController ( for : list , mastodonController : self . mastodonController )
2019-12-20 02:37:47 +00:00
listTimelineController . presentEditOnAppear = true
self . show ( listTimelineController , sender : nil )
2019-12-18 02:18:32 +00:00
}
}
} ) )
present ( alert , animated : true )
2019-12-20 02:20:29 +00:00
case let . savedHashtag ( hashtag ) :
2020-01-05 20:25:07 +00:00
show ( HashtagTimelineViewController ( for : hashtag , mastodonController : mastodonController ) , sender : nil )
2019-12-20 02:20:29 +00:00
case . addSavedHashtag :
tableView . selectRow ( at : nil , animated : true , scrollPosition : . none )
2020-01-05 20:25:07 +00:00
let navController = UINavigationController ( rootViewController : AddSavedHashtagViewController ( mastodonController : mastodonController ) )
2019-12-20 02:20:29 +00:00
present ( navController , animated : true )
2019-12-20 03:41:23 +00:00
case let . savedInstance ( url ) :
2020-01-20 16:48:47 +00:00
show ( InstanceTimelineViewController ( for : url , parentMastodonController : mastodonController ) , sender : nil )
2019-12-20 03:41:23 +00:00
case . findInstance :
tableView . selectRow ( at : nil , animated : true , scrollPosition : . none )
2020-01-20 16:48:47 +00:00
let findController = FindInstanceViewController ( parentMastodonController : mastodonController )
2019-12-20 03:41:23 +00:00
findController . instanceTimelineDelegate = self
let navController = UINavigationController ( rootViewController : findController )
present ( navController , animated : true )
2019-12-17 05:22:25 +00:00
}
}
2019-12-18 02:40:08 +00:00
override func tableView ( _ tableView : UITableView , editingStyleForRowAt indexPath : IndexPath ) -> UITableViewCell . EditingStyle {
return . delete
}
2020-03-16 03:54:04 +00:00
override func tableView ( _ tableView : UITableView , contextMenuConfigurationForRowAt indexPath : IndexPath , point : CGPoint ) -> UIContextMenuConfiguration ? {
switch dataSource . itemIdentifier ( for : indexPath ) {
case . bookmarks :
return UIContextMenuConfiguration ( identifier : nil , previewProvider : {
return BookmarksTableViewController ( mastodonController : self . mastodonController )
} , actionProvider : nil )
case let . list ( list ) :
return UIContextMenuConfiguration ( identifier : nil , previewProvider : {
return ListTimelineViewController ( for : list , mastodonController : self . mastodonController )
} , actionProvider : nil )
case let . savedHashtag ( hashtag ) :
return UIContextMenuConfiguration ( identifier : nil , previewProvider : {
return HashtagTimelineViewController ( for : hashtag , mastodonController : self . mastodonController )
} , actionProvider : nil )
case let . savedInstance ( url ) :
return UIContextMenuConfiguration ( identifier : nil , previewProvider : {
return InstanceTimelineViewController ( for : url , parentMastodonController : self . mastodonController )
} , actionProvider : nil )
default :
return nil
}
}
2019-12-17 05:22:25 +00:00
}
extension ExploreViewController {
enum Section : CaseIterable {
case bookmarks
case lists
2019-12-20 02:20:29 +00:00
case savedHashtags
2019-12-20 03:41:23 +00:00
case savedInstances
2019-12-17 05:22:25 +00:00
}
enum Item : Hashable {
case bookmarks
2019-12-17 23:48:29 +00:00
case list ( List )
2019-12-18 02:18:32 +00:00
case addList
2019-12-20 02:20:29 +00:00
case savedHashtag ( Hashtag )
case addSavedHashtag
2019-12-20 03:41:23 +00:00
case savedInstance ( URL )
case findInstance
2019-12-20 02:20:29 +00:00
2019-12-17 23:48:29 +00:00
static func = = ( lhs : ExploreViewController . Item , rhs : ExploreViewController . Item ) -> Bool {
switch ( lhs , rhs ) {
case ( . bookmarks , . bookmarks ) :
return true
case let ( . list ( a ) , . list ( b ) ) :
return a . id = = b . id
2019-12-18 02:18:32 +00:00
case ( . addList , . addList ) :
return true
2019-12-20 02:20:29 +00:00
case let ( . savedHashtag ( a ) , . savedHashtag ( b ) ) :
return a = = b
case ( . addSavedHashtag , . addSavedHashtag ) :
return true
2019-12-20 03:41:23 +00:00
case let ( . savedInstance ( a ) , . savedInstance ( b ) ) :
return a = = b
case ( . findInstance , . findInstance ) :
return true
2019-12-17 23:48:29 +00:00
default :
return false
}
}
func hash ( into hasher : inout Hasher ) {
switch self {
case . bookmarks :
hasher . combine ( " bookmarks " )
case let . list ( list ) :
hasher . combine ( " list " )
hasher . combine ( list . id )
2019-12-18 02:18:32 +00:00
case . addList :
hasher . combine ( " addList " )
2019-12-20 02:20:29 +00:00
case let . savedHashtag ( hashtag ) :
hasher . combine ( " savedHashtag " )
hasher . combine ( hashtag . name )
case . addSavedHashtag :
hasher . combine ( " addSavedHashtag " )
2019-12-20 03:41:23 +00:00
case let . savedInstance ( url ) :
hasher . combine ( " savedInstance " )
hasher . combine ( url )
case . findInstance :
hasher . combine ( " findInstance " )
2019-12-17 23:48:29 +00:00
}
}
2019-12-17 05:22:25 +00:00
}
2019-12-18 02:40:08 +00:00
2019-12-17 05:22:25 +00:00
class DataSource : UITableViewDiffableDataSource < Section , Item > {
2019-12-18 02:40:08 +00:00
weak var exploreController : ExploreViewController ?
2019-12-17 05:22:25 +00:00
override func tableView ( _ tableView : UITableView , titleForHeaderInSection section : Int ) -> String ? {
switch section {
case 1 :
return NSLocalizedString ( " Lists " , comment : " explore lists section title " )
2019-12-20 02:20:29 +00:00
case 2 :
return NSLocalizedString ( " Saved Hashtags " , comment : " explore saved hashtags section title " )
2019-12-20 03:41:23 +00:00
case 3 :
return NSLocalizedString ( " Instance Timelines " , comment : " explore instance timelines section title " )
2019-12-17 05:22:25 +00:00
default :
return nil
}
}
2019-12-18 02:40:08 +00:00
override func tableView ( _ tableView : UITableView , canEditRowAt indexPath : IndexPath ) -> Bool {
2019-12-20 02:20:29 +00:00
switch itemIdentifier ( for : indexPath ) {
case . list ( _ ) :
2019-12-18 02:40:08 +00:00
return true
2019-12-20 02:20:29 +00:00
case . savedHashtag ( _ ) :
return true
2019-12-20 03:41:23 +00:00
case . savedInstance ( _ ) :
return true
2019-12-20 02:20:29 +00:00
default :
return false
2019-12-18 02:40:08 +00:00
}
}
override func tableView ( _ tableView : UITableView , commit editingStyle : UITableViewCell . EditingStyle , forRowAt indexPath : IndexPath ) {
guard editingStyle = = . delete ,
2019-12-20 02:20:29 +00:00
let exploreController = exploreController else {
return
2019-12-18 02:40:08 +00:00
}
2019-12-20 02:20:29 +00:00
switch itemIdentifier ( for : indexPath ) {
case let . list ( list ) :
exploreController . deleteList ( list )
case let . savedHashtag ( hashtag ) :
exploreController . removeSavedHashtag ( hashtag )
2019-12-20 03:41:23 +00:00
case let . savedInstance ( url ) :
exploreController . removeSavedInstance ( url )
2019-12-20 02:20:29 +00:00
default :
return
}
2019-12-18 02:40:08 +00:00
}
2019-12-17 05:22:25 +00:00
}
}
2019-12-20 03:41:23 +00:00
extension ExploreViewController : InstanceTimelineViewControllerDelegate {
func didSaveInstance ( url : URL ) {
dismiss ( animated : true ) {
2020-01-20 16:48:47 +00:00
self . show ( InstanceTimelineViewController ( for : url , parentMastodonController : self . mastodonController ) , sender : nil )
2019-12-20 03:41:23 +00:00
}
}
func didUnsaveInstance ( url : URL ) {
dismiss ( animated : true )
}
}