mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
[ADD] Debug entries for refresh attempts, sending feedback, advanced settings, and resetting the pairing file
This commit is contained in:
committed by
Joe Mattiello
parent
07159b0ea6
commit
723c8e9539
@@ -65,6 +65,8 @@
|
||||
1F943C702927F90400ABE095 /* BrowseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BA2927E0F800B8D837 /* BrowseView.swift */; };
|
||||
1F943C712927F90400ABE095 /* MyAppsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FAFC5BD2927E10D00B8D837 /* MyAppsView.swift */; };
|
||||
1FA1C8CA294906890083119D /* MyAppsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA1C8C9294906890083119D /* MyAppsViewModel.swift */; };
|
||||
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */; };
|
||||
1FA5A6CC298E8FE4007BA946 /* MailComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */; };
|
||||
1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB84BA52928DE08006A5CF4 /* AppDetailView.swift */; };
|
||||
1FB96FBE292A20E5007E68D1 /* ObservableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FBD292A20E5007E68D1 /* ObservableScrollView.swift */; };
|
||||
1FB96FC0292A63F2007E68D1 /* AppPillButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1FB96FBF292A63F2007E68D1 /* AppPillButton.swift */; };
|
||||
@@ -606,6 +608,8 @@
|
||||
1F943C652927F36600ABE095 /* NewsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsViewModel.swift; sourceTree = "<group>"; };
|
||||
1F943C672927F39400ABE095 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = "<group>"; };
|
||||
1FA1C8C9294906890083119D /* MyAppsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewModel.swift; sourceTree = "<group>"; };
|
||||
1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshAttemptsView.swift; sourceTree = "<group>"; };
|
||||
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailComposeView.swift; sourceTree = "<group>"; };
|
||||
1FAFC5A42927E00000B8D837 /* SideStoreUIApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideStoreUIApp.swift; sourceTree = "<group>"; };
|
||||
1FAFC5B52927E06300B8D837 /* RootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootView.swift; sourceTree = "<group>"; };
|
||||
1FAFC5B82927E0EE00B8D837 /* NewsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewsView.swift; sourceTree = "<group>"; };
|
||||
@@ -1116,6 +1120,7 @@
|
||||
1F66F5B92938CA5700A910CA /* VisualEffectView.swift */,
|
||||
1F6284D6295218980060AAD8 /* DocumentPicker.swift */,
|
||||
1F545E84298D84CF00589F68 /* FilePreviewView.swift */,
|
||||
1FA5A6CB298E8FE4007BA946 /* MailComposeView.swift */,
|
||||
);
|
||||
path = "UIView Representables";
|
||||
sourceTree = "<group>";
|
||||
@@ -1201,6 +1206,7 @@
|
||||
1F0DD83E29367F6C007608A4 /* ConnectAppleIDView.swift */,
|
||||
1F2EF786297C4D40002FD839 /* LicensesView.swift */,
|
||||
1F545E82298D79E400589F68 /* ErrorLogView.swift */,
|
||||
1FA5A6C9298E8B2F007BA946 /* RefreshAttemptsView.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
@@ -2851,6 +2857,7 @@
|
||||
BF6C8FAC242935ED00125131 /* NSAttributedString+Markdown.m in Sources */,
|
||||
BFF00D322501BDA100746320 /* BackgroundRefreshAppsOperation.swift in Sources */,
|
||||
1F66F5BC2938F03700A910CA /* Modifiers.swift in Sources */,
|
||||
1FA5A6CA298E8B2F007BA946 /* RefreshAttemptsView.swift in Sources */,
|
||||
1F5DF9D82974426300DDAA47 /* AppScreenshot.swift in Sources */,
|
||||
1F66F5BA2938CA5700A910CA /* VisualEffectView.swift in Sources */,
|
||||
BF0C4EBD22A1BD8B009A2DD7 /* AppManager.swift in Sources */,
|
||||
@@ -2877,6 +2884,7 @@
|
||||
1FB84BA62928DE08006A5CF4 /* AppDetailView.swift in Sources */,
|
||||
D57DF63F271E51E400677701 /* ALTAppPatcher.m in Sources */,
|
||||
BFB6B220231870B00022A802 /* NewsCollectionViewCell.swift in Sources */,
|
||||
1FA5A6CC298E8FE4007BA946 /* MailComposeView.swift in Sources */,
|
||||
BF3BEFBF2408673400DE7D55 /* FetchProvisioningProfilesOperation.swift in Sources */,
|
||||
BFF0B69023219C6D007A79E1 /* PatreonComponents.swift in Sources */,
|
||||
1FB96FC5292A7251007E68D1 /* BrowseAppPreviewView.swift in Sources */,
|
||||
|
||||
@@ -65,7 +65,7 @@ class NotificationManager: ObservableObject {
|
||||
self.showNotification(title: text, detailText: detailText)
|
||||
}
|
||||
|
||||
func showNotification(title: String, detailText: String?) {
|
||||
func showNotification(title: String, detailText: String? = nil) {
|
||||
let notificationId = UUID()
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// MailComposeView.swift
|
||||
// SideStore
|
||||
//
|
||||
// Created by Fabian Thies on 04.02.23.
|
||||
// Copyright © 2023 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import MessageUI
|
||||
|
||||
struct MailComposeView: UIViewControllerRepresentable {
|
||||
typealias ActionHandler = () -> Void
|
||||
typealias ErrorHandler = (Error) -> Void
|
||||
|
||||
static var canSendMail: Bool {
|
||||
MFMailComposeViewController.canSendMail()
|
||||
}
|
||||
|
||||
let recipients: [String]
|
||||
let subject: String
|
||||
var body: String? = nil
|
||||
|
||||
var onMailSent: ActionHandler? = nil
|
||||
var onError: ErrorHandler? = nil
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(mailSentHandler: self.onMailSent, errorHandler: self.onError)
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> some UIViewController {
|
||||
let mailViewController = MFMailComposeViewController()
|
||||
mailViewController.mailComposeDelegate = context.coordinator
|
||||
mailViewController.setToRecipients(self.recipients)
|
||||
mailViewController.setSubject(self.subject)
|
||||
|
||||
if let body {
|
||||
mailViewController.setMessageBody(body, isHTML: false)
|
||||
}
|
||||
|
||||
return mailViewController
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension MailComposeView {
|
||||
class Coordinator: NSObject, MFMailComposeViewControllerDelegate {
|
||||
|
||||
let mailSentHandler: ActionHandler?
|
||||
let errorHandler: ErrorHandler?
|
||||
|
||||
init(mailSentHandler: ActionHandler?, errorHandler: ErrorHandler?) {
|
||||
self.mailSentHandler = mailSentHandler
|
||||
self.errorHandler = errorHandler
|
||||
super.init()
|
||||
}
|
||||
|
||||
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
|
||||
if result == .sent, let mailSentHandler {
|
||||
mailSentHandler()
|
||||
} else if result == .failed, let errorHandler, let error {
|
||||
errorHandler(error)
|
||||
}
|
||||
|
||||
controller.dismiss(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
90
AltStore/Views/Settings/RefreshAttemptsView.swift
Normal file
90
AltStore/Views/Settings/RefreshAttemptsView.swift
Normal file
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// RefreshAttemptsView.swift
|
||||
// SideStore
|
||||
//
|
||||
// Created by Fabian Thies on 04.02.23.
|
||||
// Copyright © 2023 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import AltStoreCore
|
||||
|
||||
struct RefreshAttemptsView: View {
|
||||
@SwiftUI.FetchRequest(sortDescriptors: [
|
||||
NSSortDescriptor(keyPath: \RefreshAttempt.date, ascending: false)
|
||||
])
|
||||
var refreshAttempts: FetchedResults<RefreshAttempt>
|
||||
|
||||
var groupedRefreshAttempts: [Date: [RefreshAttempt]] {
|
||||
Dictionary(grouping: refreshAttempts, by: { Calendar.current.startOfDay(for: $0.date) })
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
ForEach(groupedRefreshAttempts.keys.sorted(by: { $0 > $1 }), id: \.self) { date in
|
||||
Section {
|
||||
let attempts = groupedRefreshAttempts[date] ?? []
|
||||
ForEach(attempts, id: \.date) { attempt in
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
HStack {
|
||||
if attempt.isSuccess {
|
||||
Text("Success")
|
||||
.bold()
|
||||
.foregroundColor(.green)
|
||||
} else {
|
||||
Text("Failure")
|
||||
.bold()
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(DateFormatterHelper.timeString(for: attempt.date))
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
if let description = attempt.errorDescription {
|
||||
Text(description)
|
||||
}
|
||||
}
|
||||
}
|
||||
} header: {
|
||||
Text(DateFormatterHelper.string(for: date))
|
||||
}
|
||||
}
|
||||
}
|
||||
.background(self.listBackground)
|
||||
.navigationTitle("Refresh Attempts")
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var listBackground: some View {
|
||||
if self.refreshAttempts.isEmpty {
|
||||
VStack(spacing: 8) {
|
||||
Spacer()
|
||||
Text("No Refresh Attempts")
|
||||
.font(.title)
|
||||
|
||||
Text("The more you use SideStore, the more often iOS will allow it to refresh apps in the background.")
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.multilineTextAlignment(.center)
|
||||
.foregroundColor(.secondary)
|
||||
.padding()
|
||||
} else {
|
||||
Color.clear
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct RefreshAttemptsView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
RefreshAttemptsView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,8 @@ struct SettingsView: View {
|
||||
|
||||
@State var isShowingConnectAppleIDView = false
|
||||
@State var isShowingAddShortcutView = false
|
||||
@State var isShowingFeedbackMailView = false
|
||||
@State var isShowingResetPairingFileConfirmation = false
|
||||
|
||||
@State var externalURLToShow: URL?
|
||||
|
||||
@@ -147,18 +149,45 @@ struct SettingsView: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
SwiftUI.Button(action: switchToUIKit) {
|
||||
Text(L10n.SettingsView.switchToUIKit)
|
||||
}
|
||||
|
||||
SwiftUI.Button(action: resetImageCache) {
|
||||
Text(L10n.SettingsView.resetImageCache)
|
||||
}
|
||||
|
||||
NavigationLink {
|
||||
NavigationLink("Show Error Log") {
|
||||
ErrorLogView()
|
||||
} label: {
|
||||
Text("Show Error Log")
|
||||
}
|
||||
|
||||
NavigationLink("Show Refresh Attempts") {
|
||||
RefreshAttemptsView()
|
||||
}
|
||||
|
||||
if MailComposeView.canSendMail {
|
||||
SwiftUI.Button("Send Feedback") {
|
||||
self.isShowingFeedbackMailView = true
|
||||
}
|
||||
.sheet(isPresented: self.$isShowingFeedbackMailView) {
|
||||
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("Advanced Settings", action: self.showAdvancedSettings)
|
||||
|
||||
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()
|
||||
])
|
||||
}
|
||||
} header: {
|
||||
Text(L10n.SettingsView.debug)
|
||||
@@ -231,7 +260,6 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func switchToUIKit() {
|
||||
let storyboard = UIStoryboard(name: "Main", bundle: .main)
|
||||
let rootVC = storyboard.instantiateViewController(withIdentifier: "tabBarController") as! TabBarController
|
||||
@@ -251,6 +279,37 @@ struct SettingsView: View {
|
||||
fatalError("\(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func resetPairingFile() {
|
||||
let filename = "ALTPairingFile.mobiledevicepairing"
|
||||
let fileURL = FileManager.default.documentsDirectory.appendingPathComponent(filename)
|
||||
|
||||
// Delete the pairing file if it exists
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// Close and exit SideStore
|
||||
UIApplication.shared.perform(#selector(URLSessionTask.suspend))
|
||||
DispatchQueue.main.asyncAfter(deadline: .now().advanced(by: .milliseconds(500))) {
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
func showAdvancedSettings() {
|
||||
// Create the URL that deep links to our app's custom settings.
|
||||
guard let url = URL(string: UIApplication.openSettingsURLString) else {
|
||||
return
|
||||
}
|
||||
|
||||
// Ask the system to open that URL.
|
||||
UIApplication.shared.open(url)
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsView_Previews: PreviewProvider {
|
||||
|
||||
Reference in New Issue
Block a user