2022-11-23 22:34:02 +01:00
//
// S e t t i n g s V i e w . s w i f t
// S i d e S t o r e U I
//
// C r e a t e d b y F a b i a n T h i e s o n 1 8 . 1 1 . 2 2 .
// C o p y r i g h t © 2 0 2 2 F a b i a n T h i e s . A l l r i g h t s r e s e r v e d .
//
import SwiftUI
2022-11-27 00:26:15 +01:00
import AsyncImage
2022-12-23 15:21:16 +01:00
import SFSafeSymbols
2023-02-13 18:56:34 +01:00
import LocalConsole
2022-11-23 22:34:02 +01:00
import AltStoreCore
import Intents
struct SettingsView : View {
2023-02-19 12:03:22 -08:00
@ ObservedObject private var iO = Inject . observer
2022-11-23 22:34:02 +01:00
2022-12-12 19:12:38 +01:00
var connectedAppleID : Team ? {
DatabaseManager . shared . activeTeam ( )
}
@ SwiftUI . FetchRequest ( sortDescriptors : [ ] , predicate : NSPredicate ( format : " %K == YES " , # keyPath ( Team . isActiveTeam ) ) )
var connectedTeams : FetchedResults < Team >
2022-11-23 22:34:02 +01:00
@ AppStorage ( " isBackgroundRefreshEnabled " )
var isBackgroundRefreshEnabled : Bool = true
2023-02-19 08:06:33 -08:00
@ AppStorage ( " isDevModeEnabled " )
var isDevModeEnabled : Bool = false
2022-12-12 19:12:38 +01:00
@ State var isShowingConnectAppleIDView = false
2023-02-04 14:29:02 +01:00
@ State var isShowingResetPairingFileConfirmation = false
2023-02-19 08:06:33 -08:00
@ State var isShowingDevModePrompt = false
@ State var isShowingDevModeMenu = false
2023-01-31 22:30:21 +01:00
@ State var externalURLToShow : URL ?
2023-02-22 13:04:52 -08:00
@ State var quickLookURL : URL ?
2022-11-23 22:34:02 +01:00
2023-02-19 14:30:21 +01:00
let appVersion = Bundle . main . infoDictionary ? [ " CFBundleShortVersionString " ] as ? String ? ? " Unknown Version "
2022-12-12 19:12:38 +01:00
2022-11-23 22:34:02 +01:00
var body : some View {
List {
Section {
2022-12-12 19:12:38 +01:00
if let connectedAppleID = connectedTeams . first {
2022-11-23 22:34:02 +01:00
HStack {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . ConnectedAppleID . name )
2022-11-23 22:34:02 +01:00
. foregroundColor ( . secondary )
Spacer ( )
2022-12-12 19:12:38 +01:00
Text ( connectedAppleID . name )
2022-11-23 22:34:02 +01:00
}
HStack {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . ConnectedAppleID . eMail )
2022-11-23 22:34:02 +01:00
. foregroundColor ( . secondary )
Spacer ( )
2022-12-12 19:12:38 +01:00
Text ( connectedAppleID . account . appleID )
2022-11-23 22:34:02 +01:00
}
HStack {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . ConnectedAppleID . type )
2022-11-23 22:34:02 +01:00
. foregroundColor ( . secondary )
Spacer ( )
2022-12-12 19:12:38 +01:00
Text ( connectedAppleID . type . localizedDescription )
2022-11-23 22:34:02 +01:00
}
2022-12-12 19:12:38 +01:00
} else {
2022-11-23 22:34:02 +01:00
SwiftUI . Button {
2022-12-12 19:12:38 +01:00
self . connectAppleID ( )
2022-11-23 22:34:02 +01:00
} label : {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . connectAppleID )
2022-12-12 19:12:38 +01:00
}
}
} header : {
if ! connectedTeams . isEmpty {
HStack {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . ConnectedAppleID . text )
2022-12-12 19:12:38 +01:00
Spacer ( )
SwiftUI . Button {
self . disconnectAppleID ( )
} label : {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . ConnectedAppleID . signOut )
2022-12-12 19:12:38 +01:00
. font ( . callout )
. bold ( )
}
2022-11-23 22:34:02 +01:00
}
}
2022-12-12 19:12:38 +01:00
} footer : {
2023-01-16 19:03:33 +01:00
VStack ( alignment : . leading , spacing : 4 ) {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . ConnectedAppleID . Footer . p1 )
2022-12-12 19:12:38 +01:00
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . ConnectedAppleID . Footer . p2 )
2022-12-12 19:12:38 +01:00
}
2022-11-23 22:34:02 +01:00
}
2023-02-16 18:13:32 -08:00
Section {
NavigationLink ( L10n . AppIconsView . title ) {
AppIconsView ( )
}
}
2022-11-23 22:34:02 +01:00
Section {
Toggle ( isOn : self . $ isBackgroundRefreshEnabled , label : {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . backgroundRefresh )
2022-11-23 22:34:02 +01:00
} )
2023-02-19 14:30:21 +01:00
ModalNavigationLink ( L10n . SettingsView . addToSiri ) {
2022-12-12 19:12:38 +01:00
if let shortcut = INShortcut ( intent : INInteraction . refreshAllApps ( ) . intent ) {
SiriShortcutSetupView ( shortcut : shortcut )
2022-11-23 22:34:02 +01:00
}
}
} header : {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . refreshingApps )
2022-11-23 22:34:02 +01:00
} footer : {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . refreshingAppsFooter )
2022-11-23 22:34:02 +01:00
}
2023-01-31 22:30:21 +01:00
2022-11-23 22:34:02 +01:00
Section {
2023-01-31 22:30:21 +01:00
SwiftUI . Button {
self . externalURLToShow = URL ( string : " https://sidestore.io " ) !
} label : {
HStack {
Text ( " Developers " )
. foregroundColor ( . secondary )
Spacer ( )
Text ( " SideStore Team " )
Image ( systemSymbol : . chevronRight )
. foregroundColor ( . secondary )
}
2022-11-23 22:34:02 +01:00
}
2023-01-31 22:30:21 +01:00
. foregroundColor ( . primary )
2022-11-23 22:34:02 +01:00
2023-01-31 22:30:21 +01:00
SwiftUI . Button {
self . externalURLToShow = URL ( string : " https://fabian-thies.de " ) !
2022-11-23 22:34:02 +01:00
} label : {
HStack {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . swiftUIRedesign )
2022-11-23 22:34:02 +01:00
. foregroundColor ( . secondary )
Spacer ( )
Text ( " fabianthdev " )
2023-01-31 22:30:21 +01:00
Image ( systemSymbol : . chevronRight )
. foregroundColor ( . secondary )
2022-11-23 22:34:02 +01:00
}
}
2023-01-31 22:30:21 +01:00
. foregroundColor ( . primary )
NavigationLink {
LicensesView ( )
} label : {
Text ( " Licenses " )
}
2022-11-23 22:34:02 +01:00
} header : {
2022-12-24 21:06:28 -07:00
Text ( L10n . SettingsView . credits )
2022-11-23 22:34:02 +01:00
}
2023-01-31 22:30:21 +01:00
Section {
2023-02-04 14:29:02 +01:00
NavigationLink ( " Show Error Log " ) {
ErrorLogView ( )
2023-01-31 22:30:21 +01:00
}
2023-02-04 14:29:02 +01:00
NavigationLink ( " Show Refresh Attempts " ) {
RefreshAttemptsView ( )
2023-01-31 22:30:21 +01:00
}
2023-02-19 13:25:59 -08:00
NavigationLink ( L10n . AdvancedSettingsView . title ) {
AdvancedSettingsView ( )
}
2023-02-22 13:04:52 -08:00
AsyncFallibleButton ( action : self . exportLogs , label : { execute in Text ( L10n . SettingsView . exportLogs ) } )
2023-02-04 13:07:04 +01:00
2023-02-04 14:29:02 +01:00
if MailComposeView . canSendMail {
2023-02-19 14:30:21 +01:00
ModalNavigationLink ( " Send Feedback " ) {
2023-02-04 14:29:02 +01:00
MailComposeView ( recipients : [ " support@sidestore.io " ] ,
subject : " SideStore Beta \( appVersion ) Feedback " ) {
NotificationManager . shared . showNotification ( title : " Thank you for your feedback! " )
} onError : { error in
NotificationManager . shared . reportError ( error : error )
}
. ignoresSafeArea ( )
}
}
SwiftUI . Button ( L10n . SettingsView . switchToUIKit , action : self . switchToUIKit )
SwiftUI . Button ( L10n . SettingsView . resetImageCache , action : self . resetImageCache )
. foregroundColor ( . red )
SwiftUI . Button ( " Reset Pairing File " ) {
self . isShowingResetPairingFileConfirmation = true
}
. foregroundColor ( . red )
. actionSheet ( isPresented : self . $ isShowingResetPairingFileConfirmation ) {
ActionSheet ( title : Text ( " Are you sure to reset the pairing file? " ) , message : Text ( " You can reset the pairing file when you cannot sideload apps or enable JIT. SideStore will close when the file has been deleted. " ) , buttons : [
. destructive ( Text ( " Delete and Reset " ) , action : self . resetPairingFile ) ,
. cancel ( )
] )
2023-02-04 13:07:04 +01:00
}
2023-02-19 08:06:33 -08:00
if isDevModeEnabled {
NavigationLink ( L10n . DevModeView . title , isActive : self . $ isShowingDevModeMenu ) {
DevModeMenu ( )
2023-02-19 12:03:22 -08:00
} . foregroundColor ( . red )
2023-02-19 08:06:33 -08:00
} else {
SwiftUI . Button ( L10n . DevModeView . title ) {
self . isShowingDevModePrompt = true
}
. foregroundColor ( . red )
. sheet ( isPresented : self . $ isShowingDevModePrompt ) {
DevModePrompt ( isShowingDevModePrompt : self . $ isShowingDevModePrompt , isShowingDevModeMenu : self . $ isShowingDevModeMenu )
}
}
2023-01-31 22:30:21 +01:00
} header : {
Text ( L10n . SettingsView . debug )
}
2022-11-23 22:34:02 +01:00
Section {
} footer : {
2022-12-12 19:12:38 +01:00
Text ( " SideStore \( appVersion ) " )
2022-11-23 22:34:02 +01:00
. multilineTextAlignment ( . center )
. frame ( maxWidth : . infinity )
}
}
. listStyle ( InsetGroupedListStyle ( ) )
2022-12-24 21:06:28 -07:00
. navigationTitle ( L10n . SettingsView . title )
2022-11-23 22:34:02 +01:00
. toolbar {
ToolbarItem ( placement : . navigationBarTrailing ) {
SwiftUI . Button {
} label : {
2022-12-23 15:21:16 +01:00
Image ( systemSymbol : . personCropCircle )
2022-11-23 22:34:02 +01:00
. imageScale ( . large )
}
}
}
2023-01-31 22:30:21 +01:00
. sheet ( item : $ externalURLToShow ) { url in
SafariView ( url : url )
}
2023-02-22 13:04:52 -08:00
. quickLookPreview ( $ quickLookURL )
2023-02-19 12:03:22 -08:00
. enableInjection ( )
2022-11-23 22:34:02 +01:00
}
2022-12-12 19:12:38 +01:00
// v a r a p p l e I D S e c t i o n : s o m e V i e w {
//
// }
func connectAppleID ( ) {
2023-02-04 13:07:04 +01:00
guard let rootViewController = UIApplication . shared . keyWindow ? . rootViewController else {
return
}
AppManager . shared . authenticate ( presentingViewController : rootViewController ) { ( result ) in
2022-12-12 19:12:38 +01:00
DispatchQueue . main . async {
switch result
{
case . failure ( OperationError . cancelled ) :
// I g n o r e
break
case . failure ( let error ) :
NotificationManager . shared . reportError ( error : error )
case . success : break
}
}
}
}
func disconnectAppleID ( ) {
DatabaseManager . shared . signOut { ( error ) in
DispatchQueue . main . async {
if let error = error
{
NotificationManager . shared . reportError ( error : error )
}
}
}
}
2022-11-23 22:34:02 +01:00
func switchToUIKit ( ) {
let storyboard = UIStoryboard ( name : " Main " , bundle : . main )
let rootVC = storyboard . instantiateViewController ( withIdentifier : " tabBarController " ) as ! TabBarController
UIApplication . shared . keyWindow ? . rootViewController = rootVC
}
2022-11-27 00:26:15 +01:00
func resetImageCache ( ) {
do {
let url = try FileManager . default . url (
for : . cachesDirectory ,
in : . userDomainMask ,
appropriateFor : nil ,
create : true )
try FileManager . default . removeItem ( at : url . appendingPathComponent ( " com.zeu.cache " , isDirectory : true ) )
} catch let error {
fatalError ( " \( error ) " )
}
}
2023-02-04 14:29:02 +01:00
func resetPairingFile ( ) {
let filename = " ALTPairingFile.mobiledevicepairing "
let fileURL = FileManager . default . documentsDirectory . appendingPathComponent ( filename )
// D e l e t e t h e p a i r i n g f i l e i f i t e x i s t s
if FileManager . default . fileExists ( atPath : fileURL . path ) {
do {
try FileManager . default . removeItem ( at : fileURL )
print ( " Pairing file deleted successfully. " )
} catch {
print ( " Failed to delete pairing file: " , error )
}
}
// C l o s e a n d e x i t S i d e S t o r e
UIApplication . shared . perform ( #selector ( URLSessionTask . suspend ) )
DispatchQueue . main . asyncAfter ( deadline : . now ( ) . advanced ( by : . milliseconds ( 500 ) ) ) {
exit ( 0 )
}
}
2023-02-22 13:04:52 -08:00
func exportLogs ( ) throws {
guard let path = FileManager . default . altstoreSharedDirectory ? . appendingPathComponent ( " logs.txt " ) else { throw NSError ( domain : " Failed to get path. " , code : 1 ) }
var text = LCManager . shared . currentText
// TODO: a d d m o r e p o t e n t i a l l y s e n s i t i v e i n f o t o t h i s a r r a y l i k e U D I D
var remove = [ String ] ( )
if let connectedAppleID = connectedTeams . first {
remove . append ( connectedAppleID . name )
remove . append ( connectedAppleID . account . appleID )
remove . append ( connectedAppleID . account . firstName )
remove . append ( connectedAppleID . account . lastName )
remove . append ( connectedAppleID . account . localizedName )
remove . append ( connectedAppleID . account . identifier )
remove . append ( connectedAppleID . identifier )
}
for toRemove in remove {
text = text . replacingOccurrences ( of : toRemove , with : " [removed] " )
}
guard let data = text . data ( using : . utf8 ) else { throw NSError ( domain : " Failed to get data. " , code : 2 ) }
try data . write ( to : path )
quickLookURL = path
}
2022-11-23 22:34:02 +01:00
}
struct SettingsView_Previews : PreviewProvider {
static var previews : some View {
2023-01-16 19:03:33 +01:00
NavigationView {
SettingsView ( )
}
2022-11-23 22:34:02 +01:00
}
}
2022-11-27 00:26:15 +01:00