2019-08-28 11:13:22 -07:00
//
// P a t r e o n V i e w C o n t r o l l e r . s w i f t
// A l t S t o r e
//
2019-09-05 15:37:58 -07:00
// C r e a t e d b y R i l e y T e s t u t o n 9 / 5 / 1 9 .
2019-08-28 11:13:22 -07:00
// C o p y r i g h t © 2 0 1 9 R i l e y T e s t u t . A l l r i g h t s r e s e r v e d .
//
import UIKit
2019-09-05 15:37:58 -07:00
import SafariServices
2019-08-28 11:13:22 -07:00
import AuthenticationServices
2020-09-03 16:39:08 -07:00
import AltStoreCore
2019-08-28 11:13:22 -07:00
import Roxas
2019-09-05 15:37:58 -07:00
extension PatreonViewController
{
private enum Section : Int , CaseIterable
{
case about
case patrons
}
}
class PatreonViewController : UICollectionViewController
2019-08-28 11:13:22 -07:00
{
private lazy var dataSource = self . makeDataSource ( )
2019-09-05 15:37:58 -07:00
private lazy var patronsDataSource = self . makePatronsDataSource ( )
private var prototypeAboutHeader : AboutPatreonHeaderView !
2019-10-24 13:04:30 -07:00
override var preferredStatusBarStyle : UIStatusBarStyle {
return . lightContent
}
2019-08-28 11:13:22 -07:00
override func viewDidLoad ( )
{
super . viewDidLoad ( )
2019-09-05 15:37:58 -07:00
let aboutHeaderNib = UINib ( nibName : " AboutPatreonHeaderView " , bundle : nil )
self . prototypeAboutHeader = aboutHeaderNib . instantiate ( withOwner : nil , options : nil ) [ 0 ] as ? AboutPatreonHeaderView
self . collectionView . dataSource = self . dataSource
self . collectionView . register ( aboutHeaderNib , forSupplementaryViewOfKind : UICollectionView . elementKindSectionHeader , withReuseIdentifier : " AboutHeader " )
self . collectionView . register ( PatronsHeaderView . self , forSupplementaryViewOfKind : UICollectionView . elementKindSectionHeader , withReuseIdentifier : " PatronsHeader " )
self . collectionView . register ( PatronsFooterView . self , forSupplementaryViewOfKind : UICollectionView . elementKindSectionFooter , withReuseIdentifier : " PatronsFooter " )
2019-08-28 11:13:22 -07:00
2022-04-14 17:39:43 -07:00
NotificationCenter . default . addObserver ( self , selector : #selector ( PatreonViewController . didUpdatePatrons ( _ : ) ) , name : AppManager . didUpdatePatronsNotification , object : nil )
2019-08-28 11:13:22 -07:00
self . update ( )
}
override func viewWillAppear ( _ animated : Bool )
{
super . viewWillAppear ( animated )
self . fetchPatrons ( )
2019-09-05 15:37:58 -07:00
self . update ( )
}
override func viewDidLayoutSubviews ( )
{
super . viewDidLayoutSubviews ( )
let layout = self . collectionViewLayout as ! UICollectionViewFlowLayout
var itemWidth = ( self . collectionView . bounds . width - ( layout . sectionInset . left + layout . sectionInset . right + layout . minimumInteritemSpacing ) ) / 2
itemWidth . round ( . down )
layout . itemSize = CGSize ( width : itemWidth , height : layout . itemSize . height )
2019-08-28 11:13:22 -07:00
}
}
private extension PatreonViewController
{
2022-04-18 15:31:29 -07:00
func makeDataSource ( ) -> RSTCompositeCollectionViewDataSource < ManagedPatron >
2019-08-28 11:13:22 -07:00
{
2022-04-18 15:31:29 -07:00
let aboutDataSource = RSTDynamicCollectionViewDataSource < ManagedPatron > ( )
2019-09-05 15:37:58 -07:00
aboutDataSource . numberOfSectionsHandler = { 1 }
aboutDataSource . numberOfItemsHandler = { _ in 0 }
2019-08-28 11:13:22 -07:00
2022-04-18 15:31:29 -07:00
let dataSource = RSTCompositeCollectionViewDataSource < ManagedPatron > ( dataSources : [ aboutDataSource , self . patronsDataSource ] )
2019-09-05 15:37:58 -07:00
dataSource . proxy = self
2019-08-28 11:13:22 -07:00
return dataSource
}
2022-04-18 15:31:29 -07:00
func makePatronsDataSource ( ) -> RSTFetchedResultsCollectionViewDataSource < ManagedPatron >
2019-09-05 15:37:58 -07:00
{
2022-04-18 15:31:29 -07:00
let fetchRequest : NSFetchRequest < ManagedPatron > = ManagedPatron . fetchRequest ( )
fetchRequest . sortDescriptors = [ NSSortDescriptor ( key : # keyPath ( ManagedPatron . name ) , ascending : true , selector : #selector ( NSString . caseInsensitiveCompare ( _ : ) ) ) ]
2022-04-14 17:39:43 -07:00
2022-04-18 15:31:29 -07:00
let patronsDataSource = RSTFetchedResultsCollectionViewDataSource < ManagedPatron > ( fetchRequest : fetchRequest , managedObjectContext : DatabaseManager . shared . viewContext )
2019-09-05 15:37:58 -07:00
patronsDataSource . cellConfigurationHandler = { ( cell , patron , indexPath ) in
let cell = cell as ! PatronCollectionViewCell
cell . textLabel . text = patron . name
}
return patronsDataSource
}
2019-08-28 11:13:22 -07:00
func update ( )
{
2019-09-07 15:34:07 -07:00
self . collectionView . reloadData ( )
}
func prepare ( _ headerView : AboutPatreonHeaderView )
{
headerView . layoutMargins = self . view . layoutMargins
headerView . supportButton . addTarget ( self , action : #selector ( PatreonViewController . openPatreonURL ( _ : ) ) , for : . primaryActionTriggered )
headerView . accountButton . removeTarget ( self , action : nil , for : . primaryActionTriggered )
2019-09-12 13:08:38 -07:00
let defaultSupportButtonTitle = NSLocalizedString ( " Become a patron " , comment : " " )
let isPatronSupportButtonTitle = NSLocalizedString ( " View Patreon " , comment : " " )
let defaultText = NSLocalizedString ( " " "
Hey y ' all ,
2022-04-13 19:40:11 -07:00
You can support future development of our apps by donating to us on Patreon . In return , you ' ll receive access to the beta versions of our apps and be among the first to try the latest features .
2019-09-12 13:08:38 -07:00
Thanks for all your support 💜
2022-04-13 19:40:11 -07:00
Riley & Shane
2019-09-12 13:08:38 -07:00
" " " , comment: " " )
let isPatronText = NSLocalizedString ( " " "
Hey ,
2022-04-13 19:40:11 -07:00
You ’ re the best . Your account was linked successfully , so you now have access to the beta versions of all of our apps . You can find them all in the Browse tab .
2019-09-12 13:08:38 -07:00
Thanks for all of your support . Enjoy !
2022-04-13 19:40:11 -07:00
Riley & Shane
2019-09-12 13:08:38 -07:00
" " " , comment: " " )
2019-09-07 15:34:07 -07:00
if let account = DatabaseManager . shared . patreonAccount ( ) , PatreonAPI . shared . isAuthenticated
2019-08-28 11:13:22 -07:00
{
2019-09-07 15:34:07 -07:00
headerView . accountButton . addTarget ( self , action : #selector ( PatreonViewController . signOut ( _ : ) ) , for : . primaryActionTriggered )
headerView . accountButton . setTitle ( String ( format : NSLocalizedString ( " Unlink %@ " , comment : " " ) , account . name ) , for : . normal )
2019-09-12 13:08:38 -07:00
if account . isPatron
{
headerView . supportButton . setTitle ( isPatronSupportButtonTitle , for : . normal )
let font = UIFont . systemFont ( ofSize : 16 )
let attributedText = NSMutableAttributedString ( string : isPatronText , attributes : [ . font : font ,
. foregroundColor : UIColor . white ] )
let boldedName = NSAttributedString ( string : account . firstName ? ? account . name ,
attributes : [ . font : UIFont . boldSystemFont ( ofSize : font . pointSize ) ,
. foregroundColor : UIColor . white ] )
attributedText . insert ( boldedName , at : 4 )
headerView . textView . attributedText = attributedText
}
else
{
headerView . supportButton . setTitle ( defaultSupportButtonTitle , for : . normal )
headerView . textView . text = defaultText
}
2019-08-28 11:13:22 -07:00
}
else
{
2019-09-07 15:34:07 -07:00
headerView . accountButton . addTarget ( self , action : #selector ( PatreonViewController . authenticate ( _ : ) ) , for : . primaryActionTriggered )
2019-09-12 13:08:38 -07:00
headerView . supportButton . setTitle ( defaultSupportButtonTitle , for : . normal )
2019-09-07 15:34:07 -07:00
headerView . accountButton . setTitle ( NSLocalizedString ( " Link Patreon account " , comment : " " ) , for : . normal )
2019-09-12 13:08:38 -07:00
headerView . textView . text = defaultText
2019-08-28 11:13:22 -07:00
}
}
2019-09-05 15:37:58 -07:00
}
private extension PatreonViewController
{
@objc func fetchPatrons ( )
2019-08-28 11:13:22 -07:00
{
2022-04-14 17:39:43 -07:00
AppManager . shared . updatePatronsIfNeeded ( )
self . update ( )
2019-08-28 11:13:22 -07:00
}
2019-09-05 15:37:58 -07:00
@objc func openPatreonURL ( _ sender : UIButton )
{
2022-02-25 15:43:25 -08:00
let patreonURL = URL ( string : " https://altstore.io/patreon " ) !
2019-09-05 15:37:58 -07:00
let safariViewController = SFSafariViewController ( url : patreonURL )
safariViewController . preferredControlTintColor = self . view . tintColor
self . present ( safariViewController , animated : true , completion : nil )
}
2019-08-28 11:13:22 -07:00
@IBAction func authenticate ( _ sender : UIBarButtonItem )
{
PatreonAPI . shared . authenticate { ( result ) in
do
{
let account = try result . get ( )
try account . managedObjectContext ? . save ( )
DispatchQueue . main . async {
self . update ( )
}
}
catch ASWebAuthenticationSessionError . canceledLogin
{
// I g n o r e
}
catch
{
DispatchQueue . main . async {
2020-03-30 14:07:18 -07:00
let toastView = ToastView ( error : error )
toastView . show ( in : self )
2019-08-28 11:13:22 -07:00
}
}
}
}
@IBAction func signOut ( _ sender : UIBarButtonItem )
{
2019-09-05 15:37:58 -07:00
func signOut ( )
{
PatreonAPI . shared . signOut { ( result ) in
do
{
try result . get ( )
DispatchQueue . main . async {
self . update ( )
}
}
catch
{
DispatchQueue . main . async {
2020-03-30 14:07:18 -07:00
let toastView = ToastView ( error : error )
toastView . show ( in : self )
2019-09-05 15:37:58 -07:00
}
2019-08-28 11:13:22 -07:00
}
}
2019-09-05 15:37:58 -07:00
}
2019-09-07 15:34:07 -07:00
let alertController = UIAlertController ( title : NSLocalizedString ( " Are you sure you want to unlink your Patreon account? " , comment : " " ) , message : NSLocalizedString ( " You will no longer have access to beta versions of apps. " , comment : " " ) , preferredStyle : . actionSheet )
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " Unlink Patreon Account " , comment : " " ) , style : . destructive ) { _ in signOut ( ) } )
2019-09-05 15:37:58 -07:00
alertController . addAction ( . cancel )
self . present ( alertController , animated : true , completion : nil )
}
2022-04-14 17:39:43 -07:00
@objc func didUpdatePatrons ( _ notification : Notification )
{
DispatchQueue . main . async {
self . collectionView . reloadData ( )
}
}
2019-09-05 15:37:58 -07:00
}
extension PatreonViewController
{
override func collectionView ( _ collectionView : UICollectionView , viewForSupplementaryElementOfKind kind : String , at indexPath : IndexPath ) -> UICollectionReusableView
{
let section = Section . allCases [ indexPath . section ]
switch section
{
case . about :
let headerView = collectionView . dequeueReusableSupplementaryView ( ofKind : kind , withReuseIdentifier : " AboutHeader " , for : indexPath ) as ! AboutPatreonHeaderView
2019-09-07 15:34:07 -07:00
self . prepare ( headerView )
2019-09-05 15:37:58 -07:00
return headerView
case . patrons :
if kind = = UICollectionView . elementKindSectionHeader
2019-08-28 11:13:22 -07:00
{
2019-09-05 15:37:58 -07:00
let headerView = collectionView . dequeueReusableSupplementaryView ( ofKind : kind , withReuseIdentifier : " PatronsHeader " , for : indexPath ) as ! PatronsHeaderView
headerView . textLabel . text = NSLocalizedString ( " Special thanks to... " , comment : " " )
return headerView
}
else
{
let footerView = collectionView . dequeueReusableSupplementaryView ( ofKind : kind , withReuseIdentifier : " PatronsFooter " , for : indexPath ) as ! PatronsFooterView
footerView . button . isIndicatingActivity = false
footerView . button . isHidden = false
footerView . button . addTarget ( self , action : #selector ( PatreonViewController . fetchPatrons ) , for : . primaryActionTriggered )
2022-04-14 17:39:43 -07:00
if self . patronsDataSource . itemCount > 0
2019-09-05 15:37:58 -07:00
{
2022-04-14 17:39:43 -07:00
footerView . button . isHidden = true
}
else
{
switch AppManager . shared . updatePatronsResult
{
case . none : footerView . button . isIndicatingActivity = true
case . success ? : footerView . button . isHidden = true
case . failure ? : footerView . button . setTitle ( NSLocalizedString ( " Error Loading Patrons " , comment : " " ) , for : . normal )
}
2019-08-28 11:13:22 -07:00
}
2019-09-05 15:37:58 -07:00
return footerView
2019-08-28 11:13:22 -07:00
}
}
}
}
2019-09-05 15:37:58 -07:00
extension PatreonViewController : UICollectionViewDelegateFlowLayout
{
func collectionView ( _ collectionView : UICollectionView , layout collectionViewLayout : UICollectionViewLayout , referenceSizeForHeaderInSection section : Int ) -> CGSize
{
let section = Section . allCases [ section ]
switch section
{
case . about :
let widthConstraint = self . prototypeAboutHeader . widthAnchor . constraint ( equalToConstant : collectionView . bounds . width )
NSLayoutConstraint . activate ( [ widthConstraint ] )
defer { NSLayoutConstraint . deactivate ( [ widthConstraint ] ) }
2019-09-07 15:34:07 -07:00
self . prepare ( self . prototypeAboutHeader )
2019-09-05 15:37:58 -07:00
let size = self . prototypeAboutHeader . systemLayoutSizeFitting ( UIView . layoutFittingCompressedSize )
return size
case . patrons :
return CGSize ( width : 320 , height : 20 )
}
}
func collectionView ( _ collectionView : UICollectionView , layout collectionViewLayout : UICollectionViewLayout , referenceSizeForFooterInSection section : Int ) -> CGSize
{
let section = Section . allCases [ section ]
switch section
{
case . about : return . zero
case . patrons : return CGSize ( width : 320 , height : 20 )
}
}
}