2019-06-06 14:46:23 -07:00
//
2019-09-05 11:59:10 -07:00
// S e t t i n g s V i e w C o n t r o l l e r . s w i f t
2019-06-06 14:46:23 -07:00
// A l t S t o r e
//
2019-09-05 11:59:10 -07:00
// C r e a t e d b y R i l e y T e s t u t o n 8 / 3 1 / 1 9 .
2019-06-06 14:46:23 -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
2024-08-06 10:43:52 +09:00
import SwiftUI
2019-09-07 15:34:07 -07:00
import SafariServices
2019-09-12 13:23:21 -07:00
import MessageUI
2020-09-08 16:44:36 -07:00
import Intents
import IntentsUI
2019-06-06 14:46:23 -07:00
2020-09-03 16:39:08 -07:00
import AltStoreCore
2019-09-05 11:59:10 -07:00
extension SettingsViewController
{
fileprivate enum Section : Int , CaseIterable
{
case signIn
case account
case patreon
2020-09-08 16:44:36 -07:00
case appRefresh
2019-09-07 15:34:07 -07:00
case instructions
case credits
2019-09-05 11:59:10 -07:00
case debug
}
2019-09-07 15:34:07 -07:00
2020-09-08 16:44:36 -07:00
fileprivate enum AppRefreshRow : Int , CaseIterable
{
case backgroundRefresh
2023-11-28 00:44:47 +09:00
case noIdleTimeout
2020-09-08 16:44:36 -07:00
@ available ( iOS 14 , * )
case addToSiri
static var allCases : [ AppRefreshRow ] {
2023-11-28 00:44:47 +09:00
guard #available ( iOS 14 , * ) else { return [ . backgroundRefresh , . noIdleTimeout ] }
return [ . backgroundRefresh , . noIdleTimeout , . addToSiri ]
2020-09-08 16:44:36 -07:00
}
}
2019-09-07 15:34:07 -07:00
fileprivate enum CreditsRow : Int , CaseIterable
{
case developer
2022-04-13 20:06:57 -07:00
case operations
2019-09-07 15:34:07 -07:00
case designer
case softwareLicenses
}
2019-09-12 13:23:21 -07:00
fileprivate enum DebugRow : Int , CaseIterable
{
case sendFeedback
case refreshAttempts
2022-09-09 17:44:15 -05:00
case errorLog
2024-06-17 09:43:25 +10:00
case refreshSideJITServer
2023-11-25 15:11:22 +09:00
case clearCache
2023-01-09 15:15:31 +08:00
case resetPairingFile
2024-08-06 10:43:52 +09:00
case anisetteServers
2023-01-01 12:20:08 -05:00
case advancedSettings
2023-08-30 07:24:27 +00:00
2019-09-12 13:23:21 -07:00
}
2019-09-05 11:59:10 -07:00
}
2019-06-06 14:46:23 -07:00
2023-01-04 09:52:12 -05:00
final class SettingsViewController : UITableViewController
2019-06-06 14:46:23 -07:00
{
2019-09-05 11:59:10 -07:00
private var activeTeam : Team ?
2019-06-06 14:46:23 -07:00
2019-09-05 11:59:10 -07:00
private var prototypeHeaderFooterView : SettingsHeaderFooterView !
2019-06-06 14:46:23 -07:00
2019-09-19 22:20:10 -07:00
private var debugGestureCounter = 0
private weak var debugGestureTimer : Timer ?
2019-09-05 11:59:10 -07:00
@IBOutlet private var accountNameLabel : UILabel !
@IBOutlet private var accountEmailLabel : UILabel !
@IBOutlet private var accountTypeLabel : UILabel !
2019-06-06 14:46:23 -07:00
2019-09-05 11:59:10 -07:00
@IBOutlet private var backgroundRefreshSwitch : UISwitch !
2023-11-28 00:44:47 +09:00
@IBOutlet private var noIdleTimeoutSwitch : UISwitch !
2019-06-06 14:46:23 -07:00
2024-06-17 09:43:25 +10:00
@IBOutlet private var refreshSideJITServer : UILabel !
2020-01-13 13:32:55 -08:00
@IBOutlet private var versionLabel : UILabel !
2019-10-24 13:04:30 -07:00
override var preferredStatusBarStyle : UIStatusBarStyle {
return . lightContent
}
2019-09-19 14:43:26 -07:00
required init ? ( coder aDecoder : NSCoder )
{
super . init ( coder : aDecoder )
NotificationCenter . default . addObserver ( self , selector : #selector ( SettingsViewController . openPatreonSettings ( _ : ) ) , name : AppDelegate . openPatreonSettingsDeepLinkNotification , object : nil )
2024-08-06 10:43:52 +09:00
NotificationCenter . default . addObserver ( self , selector : #selector ( SettingsViewController . openErrorLog ( _ : ) ) , name : ToastView . openErrorLogNotification , object : nil )
2019-09-19 14:43:26 -07:00
}
2019-06-06 14:46:23 -07:00
override func viewDidLoad ( )
{
super . viewDidLoad ( )
2019-09-05 11:59:10 -07:00
let nib = UINib ( nibName : " SettingsHeaderFooterView " , bundle : nil )
self . prototypeHeaderFooterView = nib . instantiate ( withOwner : nil , options : nil ) [ 0 ] as ? SettingsHeaderFooterView
self . tableView . register ( nib , forHeaderFooterViewReuseIdentifier : " HeaderFooterView " )
2019-06-06 14:46:23 -07:00
2019-09-19 22:20:10 -07:00
let debugModeGestureRecognizer = UISwipeGestureRecognizer ( target : self , action : #selector ( SettingsViewController . handleDebugModeGesture ( _ : ) ) )
debugModeGestureRecognizer . delegate = self
debugModeGestureRecognizer . direction = . up
debugModeGestureRecognizer . numberOfTouchesRequired = 3
self . tableView . addGestureRecognizer ( debugModeGestureRecognizer )
2024-04-23 03:20:40 -04:00
var versionString : String = " "
2020-01-13 13:32:55 -08:00
if let version = Bundle . main . object ( forInfoDictionaryKey : " CFBundleShortVersionString " ) as ? String
{
2024-04-23 03:20:40 -04:00
versionString += " SideStore \( version ) "
if let xcode = Bundle . main . object ( forInfoDictionaryKey : " DTXcode " ) as ? String {
versionString += " - Xcode \( xcode ) - "
if let build = Bundle . main . object ( forInfoDictionaryKey : " DTXcodeBuild " ) as ? String {
versionString += " \( build ) "
}
}
if let pairing = Bundle . main . object ( forInfoDictionaryKey : " ALTPairingFile " ) as ? String {
let pair_test = pairing = = " <insert pairing file here> "
if ! pair_test {
versionString += " - \( ! pair_test ) "
}
}
2020-01-13 13:32:55 -08:00
}
else
{
2024-04-23 03:20:40 -04:00
versionString += " SideStore \t "
2020-01-13 13:32:55 -08:00
}
2024-08-06 10:43:52 +09:00
versionString += " \n \( Bundle . Info . appbundleIdentifier ) "
2024-04-23 03:20:40 -04:00
2024-08-06 10:43:52 +09:00
self . versionLabel . text = NSLocalizedString ( versionString , comment : " SideStore Version " )
self . versionLabel . numberOfLines = 0
self . versionLabel . lineBreakMode = . byWordWrapping
self . versionLabel . setNeedsUpdateConstraints ( )
2024-04-23 03:20:40 -04:00
self . tableView . contentInset . bottom = 40
2020-01-24 11:34:26 -08:00
2019-06-06 14:46:23 -07:00
self . update ( )
2021-10-06 12:16:47 -07:00
if #available ( iOS 15 , * ) , let appearance = self . tabBarController ? . tabBar . standardAppearance
{
2021-10-11 13:49:11 -07:00
appearance . stackedLayoutAppearance . normal . badgeBackgroundColor = . altPrimary
2021-10-06 12:16:47 -07:00
self . navigationController ? . tabBarItem . scrollEdgeAppearance = appearance
}
2019-06-06 14:46:23 -07:00
}
2019-09-07 15:34:07 -07:00
override func viewWillAppear ( _ animated : Bool )
{
super . viewWillAppear ( animated )
self . update ( )
}
2024-08-06 10:43:52 +09:00
override func prepare ( for segue : UIStoryboardSegue , sender : Any ? ) {
if segue . identifier = = " anisetteServers " {
let controller = UIHostingController ( rootView : AnisetteServers ( selected : UserDefaults . standard . menuAnisetteURL , errorCallback : {
ToastView ( text : " Cleared adi.pb! " , detailText : " You will need to log back into Apple ID in SideStore. " ) . show ( in : self )
} ) )
self . show ( controller , sender : nil )
} else {
super . prepare ( for : segue , sender : sender )
}
}
2019-06-06 14:46:23 -07:00
}
2019-07-31 11:46:26 -07:00
private extension SettingsViewController
2019-06-06 14:46:23 -07:00
{
func update ( )
{
if let team = DatabaseManager . shared . activeTeam ( )
{
2019-09-05 11:59:10 -07:00
self . accountNameLabel . text = team . name
2019-06-06 14:46:23 -07:00
self . accountEmailLabel . text = team . account . appleID
2019-09-05 11:59:10 -07:00
self . accountTypeLabel . text = team . type . localizedDescription
2019-06-06 14:46:23 -07:00
2019-09-05 11:59:10 -07:00
self . activeTeam = team
2019-06-06 14:46:23 -07:00
}
else
{
2019-09-05 11:59:10 -07:00
self . activeTeam = nil
2019-06-06 14:46:23 -07:00
}
2019-09-05 11:59:10 -07:00
self . backgroundRefreshSwitch . isOn = UserDefaults . standard . isBackgroundRefreshEnabled
2023-11-28 12:00:20 +09:00
self . noIdleTimeoutSwitch . isOn = UserDefaults . standard . isIdleTimeoutDisableEnabled
2019-09-05 11:59:10 -07:00
2019-06-06 14:46:23 -07:00
if self . isViewLoaded
{
self . tableView . reloadData ( )
}
}
2019-09-05 11:59:10 -07:00
func prepare ( _ settingsHeaderFooterView : SettingsHeaderFooterView , for section : Section , isHeader : Bool )
{
settingsHeaderFooterView . primaryLabel . isHidden = ! isHeader
settingsHeaderFooterView . secondaryLabel . isHidden = isHeader
settingsHeaderFooterView . button . isHidden = true
settingsHeaderFooterView . layoutMargins . bottom = isHeader ? 0 : 8
switch section
{
case . signIn :
if isHeader
{
settingsHeaderFooterView . primaryLabel . text = NSLocalizedString ( " ACCOUNT " , comment : " " )
}
else
{
2022-09-14 04:45:33 -07:00
settingsHeaderFooterView . secondaryLabel . text = NSLocalizedString ( " Sign in with your Apple ID to download apps from SideStore. " , comment : " " )
2019-09-05 11:59:10 -07:00
}
case . patreon :
2019-09-25 12:43:32 -07:00
if isHeader
{
2023-06-13 23:14:01 -07:00
settingsHeaderFooterView . primaryLabel . text = NSLocalizedString ( " SUPPORT US " , comment : " " )
2019-09-25 12:43:32 -07:00
}
2022-09-14 04:27:26 -07:00
else
{
2023-06-13 23:14:01 -07:00
settingsHeaderFooterView . secondaryLabel . text = NSLocalizedString ( " Support the SideStore Team by following our socials or becoming a patron! " , comment : " " )
2022-09-14 04:27:26 -07:00
}
2022-09-14 04:45:33 -07:00
2019-09-05 11:59:10 -07:00
case . account :
settingsHeaderFooterView . primaryLabel . text = NSLocalizedString ( " ACCOUNT " , comment : " " )
settingsHeaderFooterView . button . setTitle ( NSLocalizedString ( " SIGN OUT " , comment : " " ) , for : . normal )
settingsHeaderFooterView . button . addTarget ( self , action : #selector ( SettingsViewController . signOut ( _ : ) ) , for : . primaryActionTriggered )
settingsHeaderFooterView . button . isHidden = false
2020-09-08 16:44:36 -07:00
case . appRefresh :
if isHeader
{
settingsHeaderFooterView . primaryLabel . text = NSLocalizedString ( " REFRESHING APPS " , comment : " " )
}
else
{
2023-11-27 23:18:36 -08:00
settingsHeaderFooterView . secondaryLabel . text = NSLocalizedString ( " Enable Background Refresh to automatically refresh apps in the background when connected to Wi-Fi. \n \n Disable the Idle Timeout toggle to allow SideStore to not let your device go to sleep during a refresh or install of any apps. " , comment : " " )
2020-09-08 16:44:36 -07:00
}
2019-09-05 11:59:10 -07:00
2019-09-07 15:34:07 -07:00
case . instructions :
break
case . credits :
settingsHeaderFooterView . primaryLabel . text = NSLocalizedString ( " CREDITS " , comment : " " )
2019-09-05 11:59:10 -07:00
case . debug :
settingsHeaderFooterView . primaryLabel . text = NSLocalizedString ( " DEBUG " , comment : " " )
}
}
func preferredHeight ( for settingsHeaderFooterView : SettingsHeaderFooterView , in section : Section , isHeader : Bool ) -> CGFloat
{
let widthConstraint = settingsHeaderFooterView . contentView . widthAnchor . constraint ( equalToConstant : tableView . bounds . width )
NSLayoutConstraint . activate ( [ widthConstraint ] )
defer { NSLayoutConstraint . deactivate ( [ widthConstraint ] ) }
self . prepare ( settingsHeaderFooterView , for : section , isHeader : isHeader )
let size = settingsHeaderFooterView . contentView . systemLayoutSizeFitting ( UIView . layoutFittingCompressedSize )
return size . height
}
2019-06-06 14:46:23 -07:00
}
2019-07-31 11:46:26 -07:00
private extension SettingsViewController
2019-06-06 14:46:23 -07:00
{
2019-09-05 11:59:10 -07:00
func signIn ( )
2019-06-06 14:46:23 -07:00
{
AppManager . shared . authenticate ( presentingViewController : self ) { ( result ) in
DispatchQueue . main . async {
2019-11-18 14:49:17 -08:00
switch result
{
case . failure ( OperationError . cancelled ) :
// I g n o r e
break
case . failure ( let error ) :
2020-03-30 14:07:18 -07:00
let toastView = ToastView ( error : error )
toastView . show ( in : self )
2019-11-18 14:49:17 -08:00
case . success : break
}
2019-06-06 14:46:23 -07:00
self . update ( )
}
}
}
2019-09-05 11:59:10 -07:00
@objc func signOut ( _ sender : UIBarButtonItem )
2019-06-06 14:46:23 -07:00
{
func signOut ( )
{
DatabaseManager . shared . signOut { ( error ) in
DispatchQueue . main . async {
if let error = error
{
2020-03-30 14:07:18 -07:00
let toastView = ToastView ( error : error )
toastView . show ( in : self )
2019-06-06 14:46:23 -07:00
}
self . update ( )
}
}
}
let alertController = UIAlertController ( title : NSLocalizedString ( " Are you sure you want to sign out? " , comment : " " ) , message : NSLocalizedString ( " You will no longer be able to install or refresh apps once you sign out. " , comment : " " ) , preferredStyle : . actionSheet )
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " Sign Out " , comment : " " ) , style : . destructive ) { _ in signOut ( ) } )
alertController . addAction ( . cancel )
2023-01-09 15:15:31 +08:00
// F i x c r a s h o n i P a d
alertController . popoverPresentationController ? . barButtonItem = sender
2019-06-06 14:46:23 -07:00
self . present ( alertController , animated : true , completion : nil )
}
2019-09-05 11:59:10 -07:00
@IBAction func toggleIsBackgroundRefreshEnabled ( _ sender : UISwitch )
{
UserDefaults . standard . isBackgroundRefreshEnabled = sender . isOn
}
2019-09-19 22:20:10 -07:00
2023-11-28 00:44:47 +09:00
@IBAction func toggleNoIdleTimeoutEnabled ( _ sender : UISwitch )
{
UserDefaults . standard . isIdleTimeoutDisableEnabled = sender . isOn
}
2020-09-08 16:44:36 -07:00
@ available ( iOS 14 , * )
@IBAction func addRefreshAppsShortcut ( )
{
guard let shortcut = INShortcut ( intent : INInteraction . refreshAllApps ( ) . intent ) else { return }
let viewController = INUIAddVoiceShortcutViewController ( shortcut : shortcut )
viewController . delegate = self
viewController . modalPresentationStyle = . formSheet
self . present ( viewController , animated : true , completion : nil )
}
2023-02-07 16:11:39 -06:00
func clearCache ( )
{
2023-08-30 05:12:02 +00:00
let alertController = UIAlertController ( title : NSLocalizedString ( " Are you sure you want to clear SideStore's cache? " , comment : " " ) ,
2023-02-07 16:11:39 -06:00
message : NSLocalizedString ( " This will remove all temporary files as well as backups for uninstalled apps. " , comment : " " ) ,
preferredStyle : . actionSheet )
alertController . addAction ( UIAlertAction ( title : UIAlertAction . cancel . title , style : UIAlertAction . cancel . style ) { [ weak self ] _ in
self ? . tableView . indexPathForSelectedRow . map { self ? . tableView . deselectRow ( at : $0 , animated : true ) }
} )
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " Clear Cache " , comment : " " ) , style : . destructive ) { [ weak self ] _ in
AppManager . shared . clearAppCache { result in
DispatchQueue . main . async {
self ? . tableView . indexPathForSelectedRow . map { self ? . tableView . deselectRow ( at : $0 , animated : true ) }
switch result
{
case . success : break
case . failure ( let error ) :
let alertController = UIAlertController ( title : NSLocalizedString ( " Unable to Clear Cache " , comment : " " ) , message : error . localizedDescription , preferredStyle : . alert )
alertController . addAction ( . ok )
self ? . present ( alertController , animated : true )
}
}
}
} )
2023-11-26 13:50:45 +09:00
if let popoverController = alertController . popoverPresentationController {
popoverController . sourceView = self . view
popoverController . sourceRect = CGRect ( x : self . view . bounds . midX , y : self . view . bounds . midY , width : 0 , height : 0 )
}
2023-02-07 16:11:39 -06:00
self . present ( alertController , animated : true )
}
2019-09-19 22:20:10 -07:00
@IBAction func handleDebugModeGesture ( _ gestureRecognizer : UISwipeGestureRecognizer )
{
self . debugGestureCounter += 1
self . debugGestureTimer ? . invalidate ( )
if self . debugGestureCounter >= 3
{
self . debugGestureCounter = 0
UserDefaults . standard . isDebugModeEnabled . toggle ( )
self . tableView . reloadData ( )
}
else
{
self . debugGestureTimer = Timer . scheduledTimer ( withTimeInterval : 0.4 , repeats : false ) { [ weak self ] ( timer ) in
self ? . debugGestureCounter = 0
}
}
}
2019-09-21 13:57:18 -07:00
func openTwitter ( username : String )
{
let twitterAppURL = URL ( string : " twitter://user?screen_name= " + username ) !
UIApplication . shared . open ( twitterAppURL , options : [ : ] ) { ( success ) in
if success
{
if let selectedIndexPath = self . tableView . indexPathForSelectedRow
{
self . tableView . deselectRow ( at : selectedIndexPath , animated : true )
}
}
else
{
let safariURL = URL ( string : " https://twitter.com/ " + username ) !
let safariViewController = SFSafariViewController ( url : safariURL )
safariViewController . preferredControlTintColor = . altPrimary
self . present ( safariViewController , animated : true , completion : nil )
}
}
}
2019-06-06 14:46:23 -07:00
}
2019-09-19 14:43:26 -07:00
private extension SettingsViewController
{
@objc func openPatreonSettings ( _ notification : Notification )
{
guard self . presentedViewController = = nil else { return }
UIView . performWithoutAnimation {
self . navigationController ? . popViewController ( animated : false )
self . performSegue ( withIdentifier : " showPatreon " , sender : nil )
}
}
2024-08-06 10:43:52 +09:00
@objc func openErrorLog ( _ : Notification ) {
guard self . presentedViewController = = nil else { return }
self . navigationController ? . popViewController ( animated : false )
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + 0.1 ) {
self . performSegue ( withIdentifier : " showErrorLog " , sender : nil )
}
}
2019-09-19 14:43:26 -07:00
}
2019-07-31 11:46:26 -07:00
extension SettingsViewController
2019-06-06 14:46:23 -07:00
{
2019-09-19 22:20:10 -07:00
override func numberOfSections ( in tableView : UITableView ) -> Int
{
var numberOfSections = super . numberOfSections ( in : tableView )
if ! UserDefaults . standard . isDebugModeEnabled
{
numberOfSections -= 1
}
return numberOfSections
}
2019-09-05 11:59:10 -07:00
override func tableView ( _ tableView : UITableView , numberOfRowsInSection section : Int ) -> Int
2019-06-06 14:46:23 -07:00
{
2019-09-05 11:59:10 -07:00
let section = Section . allCases [ section ]
switch section
{
case . signIn : return ( self . activeTeam = = nil ) ? 1 : 0
case . account : return ( self . activeTeam = = nil ) ? 0 : 3
2020-09-08 16:44:36 -07:00
case . appRefresh : return AppRefreshRow . allCases . count
2019-09-05 11:59:10 -07:00
default : return super . tableView ( tableView , numberOfRowsInSection : section . rawValue )
}
}
2020-09-08 17:11:22 -07:00
override func tableView ( _ tableView : UITableView , cellForRowAt indexPath : IndexPath ) -> UITableViewCell
{
let cell = super . tableView ( tableView , cellForRowAt : indexPath )
2024-08-06 10:43:52 +09:00
if #available ( iOS 14 , * ) { }
else if let cell = cell as ? InsetGroupTableViewCell ,
indexPath . section = = Section . appRefresh . rawValue ,
indexPath . row = = AppRefreshRow . backgroundRefresh . rawValue
{
// O n l y o n e r o w i s v i s i b l e p r e - i O S 1 4 .
cell . style = . single
}
2023-11-28 00:44:47 +09:00
if AppRefreshRow . AllCases ( ) . count = = 1
2020-09-08 17:11:22 -07:00
{
2023-11-28 00:44:47 +09:00
if let cell = cell as ? InsetGroupTableViewCell ,
indexPath . section = = Section . appRefresh . rawValue ,
indexPath . row = = AppRefreshRow . backgroundRefresh . rawValue
{
cell . style = . single
}
2020-09-08 17:11:22 -07:00
}
2023-11-28 00:44:47 +09:00
2020-09-08 17:11:22 -07:00
return cell
}
2019-09-05 11:59:10 -07:00
override func tableView ( _ tableView : UITableView , viewForHeaderInSection section : Int ) -> UIView ?
{
let section = Section . allCases [ section ]
switch section
{
case . signIn where self . activeTeam != nil : return nil
case . account where self . activeTeam = = nil : return nil
2020-10-07 11:32:47 -07:00
case . signIn , . account , . patreon , . appRefresh , . credits , . debug :
2019-09-05 11:59:10 -07:00
let headerView = tableView . dequeueReusableHeaderFooterView ( withIdentifier : " HeaderFooterView " ) as ! SettingsHeaderFooterView
self . prepare ( headerView , for : section , isHeader : true )
return headerView
2020-09-08 16:44:36 -07:00
case . instructions : return nil
2019-09-05 11:59:10 -07:00
}
}
override func tableView ( _ tableView : UITableView , viewForFooterInSection section : Int ) -> UIView ?
{
let section = Section . allCases [ section ]
switch section
{
case . signIn where self . activeTeam != nil : return nil
2020-10-07 11:32:47 -07:00
case . signIn , . patreon , . appRefresh :
2019-09-05 11:59:10 -07:00
let footerView = tableView . dequeueReusableHeaderFooterView ( withIdentifier : " HeaderFooterView " ) as ! SettingsHeaderFooterView
self . prepare ( footerView , for : section , isHeader : false )
return footerView
2019-09-07 15:34:07 -07:00
case . account , . credits , . debug , . instructions : return nil
2019-09-05 11:59:10 -07:00
}
2019-06-06 14:46:23 -07:00
}
2019-09-05 11:59:10 -07:00
override func tableView ( _ tableView : UITableView , heightForHeaderInSection section : Int ) -> CGFloat
{
let section = Section . allCases [ section ]
switch section
{
case . signIn where self . activeTeam != nil : return 1.0
case . account where self . activeTeam = = nil : return 1.0
2020-10-07 11:32:47 -07:00
case . signIn , . account , . patreon , . appRefresh , . credits , . debug :
2019-09-05 11:59:10 -07:00
let height = self . preferredHeight ( for : self . prototypeHeaderFooterView , in : section , isHeader : true )
return height
2020-09-08 16:44:36 -07:00
case . instructions : return 0.0
2019-09-05 11:59:10 -07:00
}
}
override func tableView ( _ tableView : UITableView , heightForFooterInSection section : Int ) -> CGFloat
{
let section = Section . allCases [ section ]
switch section
{
case . signIn where self . activeTeam != nil : return 1.0
2024-08-13 14:15:31 -04:00
case . account where self . activeTeam = = nil : return 1.0
2020-10-07 11:32:47 -07:00
case . signIn , . patreon , . appRefresh :
2019-09-05 11:59:10 -07:00
let height = self . preferredHeight ( for : self . prototypeHeaderFooterView , in : section , isHeader : false )
return height
2019-09-07 15:34:07 -07:00
case . account , . credits , . debug , . instructions : return 0.0
2019-09-05 11:59:10 -07:00
}
}
}
2019-06-06 14:46:23 -07:00
2019-09-05 11:59:10 -07:00
extension SettingsViewController
{
override func tableView ( _ tableView : UITableView , didSelectRowAt indexPath : IndexPath )
{
let section = Section . allCases [ indexPath . section ]
switch section
{
case . signIn : self . signIn ( )
2019-09-07 15:34:07 -07:00
case . instructions : break
2020-09-08 16:44:36 -07:00
case . appRefresh :
let row = AppRefreshRow . allCases [ indexPath . row ]
switch row
{
case . backgroundRefresh : break
2023-11-28 00:44:47 +09:00
case . noIdleTimeout : break
2020-09-08 16:44:36 -07:00
case . addToSiri :
guard #available ( iOS 14 , * ) else { return }
self . addRefreshAppsShortcut ( )
}
2023-02-07 16:11:39 -06:00
2019-09-07 15:34:07 -07:00
case . credits :
let row = CreditsRow . allCases [ indexPath . row ]
switch row
{
2022-11-05 23:50:07 -07:00
case . developer : self . openTwitter ( username : " sidestore_io " )
case . operations : self . openTwitter ( username : " sidestore_io " )
2022-12-11 12:03:45 -05:00
case . designer : self . openTwitter ( username : " lit_ritt " )
2019-09-21 13:57:18 -07:00
case . softwareLicenses : break
2019-09-07 15:34:07 -07:00
}
2019-09-12 13:23:21 -07:00
case . debug :
let row = DebugRow . allCases [ indexPath . row ]
switch row
{
case . sendFeedback :
2024-08-12 21:54:34 -04:00
let alertController = UIAlertController ( title : " Send Feedback " , message : " Choose a method to send feedback: " , preferredStyle : . actionSheet )
// O p t i o n 1 : G i t H u b
alertController . addAction ( UIAlertAction ( title : " GitHub " , style : . default ) { _ in
if let githubURL = URL ( string : " https://github.com/SideStore/SideStore/issues " ) {
let safariViewController = SFSafariViewController ( url : githubURL )
safariViewController . preferredControlTintColor = . altPrimary
self . present ( safariViewController , animated : true , completion : nil )
2019-09-12 13:23:21 -07:00
}
2024-08-12 21:54:34 -04:00
} )
// O p t i o n 2 : D i s c o r d
alertController . addAction ( UIAlertAction ( title : " Discord " , style : . default ) { _ in
if let discordURL = URL ( string : " https://discord.gg/sidestore-949183273383395328 " ) {
let safariViewController = SFSafariViewController ( url : discordURL )
safariViewController . preferredControlTintColor = . altPrimary
self . present ( safariViewController , animated : true , completion : nil )
2019-09-12 13:23:21 -07:00
}
2024-08-12 21:54:34 -04:00
} )
// O p t i o n 3 : M a i l
2024-08-13 14:15:31 -04:00
// a l e r t C o n t r o l l e r . a d d A c t i o n ( U I A l e r t A c t i o n ( t i t l e : " S e n d E m a i l " , s t y l e : . d e f a u l t ) { _ i n
// i f M F M a i l C o m p o s e V i e w C o n t r o l l e r . c a n S e n d M a i l ( ) {
// l e t m a i l V i e w C o n t r o l l e r = M F M a i l C o m p o s e V i e w C o n t r o l l e r ( )
// m a i l V i e w C o n t r o l l e r . m a i l C o m p o s e D e l e g a t e = s e l f
// m a i l V i e w C o n t r o l l e r . s e t T o R e c i p i e n t s ( [ " s u p p o r t @ s i d e s t o r e . i o " ] )
//
// i f l e t v e r s i o n = B u n d l e . m a i n . o b j e c t ( f o r I n f o D i c t i o n a r y K e y : " C F B u n d l e S h o r t V e r s i o n S t r i n g " ) a s ? S t r i n g {
// m a i l V i e w C o n t r o l l e r . s e t S u b j e c t ( " S i d e S t o r e B e t a \ ( v e r s i o n ) F e e d b a c k " )
// } e l s e {
// m a i l V i e w C o n t r o l l e r . s e t S u b j e c t ( " S i d e S t o r e B e t a F e e d b a c k " )
// }
//
// s e l f . p r e s e n t ( m a i l V i e w C o n t r o l l e r , a n i m a t e d : t r u e , c o m p l e t i o n : n i l )
// } e l s e {
// l e t t o a s t V i e w = T o a s t V i e w ( t e x t : N S L o c a l i z e d S t r i n g ( " C a n n o t S e n d M a i l " , c o m m e n t : " " ) , d e t a i l T e x t : n i l )
// t o a s t V i e w . s h o w ( i n : s e l f )
// }
// } )
2024-08-12 21:54:34 -04:00
// C a n c e l a c t i o n
alertController . addAction ( UIAlertAction ( title : " Cancel " , style : . cancel , handler : nil ) )
// F o r i P a d : S e t t h e s o u r c e v i e w i f p r e s e n t i n g o n i P a d t o a v o i d c r a s h e s
if let popoverController = alertController . popoverPresentationController {
popoverController . sourceView = self . view
popoverController . sourceRect = self . view . bounds
2019-09-12 13:23:21 -07:00
}
2023-08-30 07:09:52 +00:00
2024-08-12 21:54:34 -04:00
// P r e s e n t t h e a c t i o n s h e e t
self . present ( alertController , animated : true , completion : nil )
2024-06-17 09:43:25 +10:00
case . refreshSideJITServer :
if #available ( iOS 17 , * ) {
let alertController = UIAlertController (
title : NSLocalizedString ( " Are you sure to Refresh SideJITServer? " , comment : " " ) ,
message : NSLocalizedString ( " if you do not have SideJITServer setup this will do nothing " , comment : " " ) ,
preferredStyle : UIAlertController . Style . actionSheet )
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " Refresh " , comment : " " ) , style : . destructive ) { _ in
if UserDefaults . standard . sidejitenable {
var SJSURL = " "
if ( UserDefaults . standard . textInputSideJITServerurl ? ? " " ) . isEmpty {
SJSURL = " http://sidejitserver._http._tcp.local:8080 "
} else {
SJSURL = UserDefaults . standard . textInputSideJITServerurl ? ? " "
} // r e p l a c e w i t h y o u r U R L
let url = URL ( string : SJSURL + " /re/ " ) !
let task = URLSession . shared . dataTask ( with : url ) { ( data , response , error ) in
if let error = error {
print ( " Error: \( error ) " )
} else {
// D o n o t h i n g w i t h d a t a o r r e s p o n s e
}
}
task . resume ( )
}
} )
alertController . addAction ( . cancel )
// F i x c r a s h o n i P a d
alertController . popoverPresentationController ? . sourceView = self . tableView
alertController . popoverPresentationController ? . sourceRect = self . tableView . rectForRow ( at : indexPath )
self . present ( alertController , animated : true )
self . tableView . deselectRow ( at : indexPath , animated : true )
} else {
let alertController = UIAlertController (
title : NSLocalizedString ( " You are not on iOS 17+ This will not work " , comment : " " ) ,
message : NSLocalizedString ( " This is meant for 'SideJITServer' and it only works on iOS 17+ " , comment : " " ) ,
preferredStyle : UIAlertController . Style . actionSheet )
2023-08-30 04:47:36 +00:00
2024-06-17 09:43:25 +10:00
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " OK " , comment : " " ) , style : . destructive ) { _ in
print ( " Not on iOS 17 " )
} )
alertController . addAction ( . cancel )
// F i x c r a s h o n i P a d
alertController . popoverPresentationController ? . sourceView = self . tableView
alertController . popoverPresentationController ? . sourceRect = self . tableView . rectForRow ( at : indexPath )
self . present ( alertController , animated : true )
self . tableView . deselectRow ( at : indexPath , animated : true )
}
case . clearCache : self . clearCache ( )
2023-01-09 15:15:31 +08:00
case . resetPairingFile :
let filename = " ALTPairingFile.mobiledevicepairing "
let fm = FileManager . default
let documentsPath = fm . documentsDirectory . appendingPathComponent ( " / \( filename ) " )
let alertController = UIAlertController (
title : NSLocalizedString ( " Are you sure to reset the pairing file? " , comment : " " ) ,
message : NSLocalizedString ( " You can reset the pairing file when you cannot sideload apps or enable JIT. You need to restart SideStore. " , comment : " " ) ,
preferredStyle : UIAlertController . Style . actionSheet )
alertController . addAction ( UIAlertAction ( title : NSLocalizedString ( " Delete and Reset " , comment : " " ) , style : . destructive ) { _ in
if fm . fileExists ( atPath : documentsPath . path ) , let contents = try ? String ( contentsOf : documentsPath ) , ! contents . isEmpty {
2024-02-23 19:46:31 -05:00
UserDefaults . standard . isPairingReset = true
2023-01-09 15:15:31 +08:00
try ? fm . removeItem ( atPath : documentsPath . path )
NSLog ( " Pairing File Reseted " )
}
self . tableView . deselectRow ( at : indexPath , animated : true )
2024-02-23 19:46:31 -05:00
let dialogMessage = UIAlertController ( title : NSLocalizedString ( " Pairing File Reset " , comment : " " ) , message : NSLocalizedString ( " Please restart SideStore " , comment : " " ) , preferredStyle : . alert )
2023-01-09 15:15:31 +08:00
self . present ( dialogMessage , animated : true , completion : nil )
} )
alertController . addAction ( . cancel )
// F i x c r a s h o n i P a d
alertController . popoverPresentationController ? . sourceView = self . tableView
alertController . popoverPresentationController ? . sourceRect = self . tableView . rectForRow ( at : indexPath )
self . present ( alertController , animated : true )
self . tableView . deselectRow ( at : indexPath , animated : true )
2024-08-06 10:43:52 +09:00
case . anisetteServers :
self . prepare ( for : UIStoryboardSegue ( identifier : " anisetteServers " , source : self , destination : UIHostingController ( rootView : AnisetteServers ( selected : " " , errorCallback : {
ToastView ( text : " Reset adi.pb " , detailText : " Buh " ) . show ( in : self )
} ) ) ) , sender : nil )
// s e l f . p e r f o r m S e g u e ( w i t h I d e n t i f i e r : " a n i s e t t e S e r v e r s " , s e n d e r : n i l )
2023-01-01 12:20:08 -05:00
case . advancedSettings :
// C r e a t e t h e U R L t h a t d e e p l i n k s t o y o u r a p p ' s c u s t o m s e t t i n g s .
if let url = URL ( string : UIApplication . openSettingsURLString ) {
// A s k t h e s y s t e m t o o p e n t h a t U R L .
UIApplication . shared . open ( url )
} else {
ELOG ( " UIApplication.openSettingsURLString invalid " )
}
2022-09-09 17:44:15 -05:00
case . refreshAttempts , . errorLog : break
2023-08-30 07:24:27 +00:00
2019-09-12 13:23:21 -07:00
}
2019-09-05 11:59:10 -07:00
default : break
}
}
}
2019-09-12 13:23:21 -07:00
extension SettingsViewController : MFMailComposeViewControllerDelegate
{
func mailComposeController ( _ controller : MFMailComposeViewController , didFinishWith result : MFMailComposeResult , error : Error ? )
{
if let error = error
{
2020-03-30 14:07:18 -07:00
let toastView = ToastView ( error : error )
toastView . show ( in : self )
2019-09-12 13:23:21 -07:00
}
controller . dismiss ( animated : true , completion : nil )
}
}
2019-09-19 22:20:10 -07:00
extension SettingsViewController : UIGestureRecognizerDelegate
{
func gestureRecognizer ( _ gestureRecognizer : UIGestureRecognizer , shouldRecognizeSimultaneouslyWith otherGestureRecognizer : UIGestureRecognizer ) -> Bool
{
return true
}
}
2020-09-08 16:44:36 -07:00
extension SettingsViewController : INUIAddVoiceShortcutViewControllerDelegate
{
func addVoiceShortcutViewController ( _ controller : INUIAddVoiceShortcutViewController , didFinishWith voiceShortcut : INVoiceShortcut ? , error : Error ? )
{
if let indexPath = self . tableView . indexPathForSelectedRow
{
self . tableView . deselectRow ( at : indexPath , animated : true )
}
controller . dismiss ( animated : true , completion : nil )
guard let error = error else { return }
let toastView = ToastView ( error : error )
toastView . show ( in : self )
}
func addVoiceShortcutViewControllerDidCancel ( _ controller : INUIAddVoiceShortcutViewController )
{
if let indexPath = self . tableView . indexPathForSelectedRow
{
self . tableView . deselectRow ( at : indexPath , animated : true )
}
controller . dismiss ( animated : true , completion : nil )
}
}