diff --git a/AltStore/App Detail/AppViewController.swift b/AltStore/App Detail/AppViewController.swift index ccc6b4eb..a68aec57 100644 --- a/AltStore/App Detail/AppViewController.swift +++ b/AltStore/App Detail/AppViewController.swift @@ -511,6 +511,7 @@ extension AppViewController { DispatchQueue.main.async { let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) } } diff --git a/AltStore/Browse/BrowseViewController.swift b/AltStore/Browse/BrowseViewController.swift index 15f3c17c..27e7d283 100644 --- a/AltStore/Browse/BrowseViewController.swift +++ b/AltStore/Browse/BrowseViewController.swift @@ -279,6 +279,7 @@ private extension BrowseViewController case .failure(OperationError.cancelled): break // Ignore case .failure(let error): let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) case .success: print("Installed app:", app.bundleIdentifier) diff --git a/AltStore/Components/ToastView.swift b/AltStore/Components/ToastView.swift index f2ea222c..acb51b3a 100644 --- a/AltStore/Components/ToastView.swift +++ b/AltStore/Components/ToastView.swift @@ -16,10 +16,17 @@ extension TimeInterval static let longToastViewDuration = 8.0 } +extension ToastView +{ + static let openErrorLogNotification = Notification.Name("ALTOpenErrorLogNotification") +} + class ToastView: RSTToastView { var preferredDuration: TimeInterval + var opensErrorLog: Bool = false + override init(text: String, detailText detailedText: String?) { if detailedText == nil @@ -43,7 +50,10 @@ class ToastView: RSTToastView // RSTToastView does not expose stack view containing labels, // so we access it indirectly as the labels' superview. stackView.spacing = (detailedText != nil) ? 4.0 : 0.0 + stackView.alignment = .leading } + + self.addTarget(self, action: #selector(ToastView.showErrorLog), for: .touchUpInside) } convenience init(error: Error) @@ -99,6 +109,23 @@ class ToastView: RSTToastView override func show(in view: UIView, duration: TimeInterval) { + if self.opensErrorLog, #available(iOS 13, *), + case let configuration = UIImage.SymbolConfiguration(font: self.textLabel.font), + let icon = UIImage(systemName: "chevron.right.circle", withConfiguration: configuration) + { + let tintedIcon = icon.withTintColor(.white, renderingMode: .alwaysOriginal) + + let moreIconImageView = UIImageView(image: tintedIcon) + moreIconImageView.translatesAutoresizingMaskIntoConstraints = false + self.addSubview(moreIconImageView) + + NSLayoutConstraint.activate([ + moreIconImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -self.layoutMargins.right), + moreIconImageView.centerYAnchor.constraint(equalTo: self.textLabel.centerYAnchor), + moreIconImageView.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: self.textLabel.trailingAnchor, multiplier: 1.0) + ]) + } + super.show(in: view, duration: duration) let announcement = (self.textLabel.text ?? "") + ". " + (self.detailTextLabel.text ?? "") @@ -115,3 +142,13 @@ class ToastView: RSTToastView self.show(in: view, duration: self.preferredDuration) } } + +private extension ToastView +{ + @objc func showErrorLog() + { + guard self.opensErrorLog else { return } + + NotificationCenter.default.post(name: ToastView.openErrorLogNotification, object: self) + } +} diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift index 32cd2bc3..40d3d844 100644 --- a/AltStore/My Apps/MyAppsViewController.swift +++ b/AltStore/My Apps/MyAppsViewController.swift @@ -555,6 +555,8 @@ private extension MyAppsViewController toastView.preferredDuration = 4.0 } + toastView.opensErrorLog = true + toastView.show(in: self) } @@ -690,6 +692,7 @@ private extension MyAppsViewController case .failure(let error): let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) self.collectionView.reloadItems(at: [indexPath]) @@ -899,6 +902,7 @@ private extension MyAppsViewController case .failure(let error): let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) completion(.failure(error)) @@ -1063,6 +1067,7 @@ private extension MyAppsViewController installedApp.isActive = false let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) } } @@ -1136,6 +1141,7 @@ private extension MyAppsViewController installedApp.isActive = true let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) } } @@ -1168,6 +1174,7 @@ private extension MyAppsViewController case .failure(let error): DispatchQueue.main.async { let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) } } @@ -1201,6 +1208,7 @@ private extension MyAppsViewController DispatchQueue.main.async { let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue]) @@ -1236,6 +1244,7 @@ private extension MyAppsViewController DispatchQueue.main.async { let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) } } @@ -1325,6 +1334,7 @@ private extension MyAppsViewController case .success: break case .failure(let error): let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) } } diff --git a/AltStore/News/NewsViewController.swift b/AltStore/News/NewsViewController.swift index 0cf77a9c..e41dcad0 100644 --- a/AltStore/News/NewsViewController.swift +++ b/AltStore/News/NewsViewController.swift @@ -314,6 +314,7 @@ private extension NewsViewController case .failure(OperationError.cancelled): break // Ignore case .failure(let error): let toastView = ToastView(error: error) + toastView.opensErrorLog = true toastView.show(in: self) case .success: print("Installed app:", storeApp.bundleIdentifier) diff --git a/AltStore/Settings/SettingsViewController.swift b/AltStore/Settings/SettingsViewController.swift index 41711393..12902e36 100644 --- a/AltStore/Settings/SettingsViewController.swift +++ b/AltStore/Settings/SettingsViewController.swift @@ -82,6 +82,7 @@ class SettingsViewController: UITableViewController super.init(coder: aDecoder) NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.openPatreonSettings(_:)), name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.openErrorLog(_:)), name: ToastView.openErrorLogNotification, object: nil) } override func viewDidLoad() @@ -340,6 +341,17 @@ private extension SettingsViewController self.performSegue(withIdentifier: "showPatreon", sender: nil) } } + + @objc func openErrorLog(_ notification: Notification) + { + guard self.presentedViewController == nil else { return } + + self.navigationController?.popViewController(animated: false) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + self.performSegue(withIdentifier: "showErrorLog", sender: nil) + } + } } extension SettingsViewController diff --git a/AltStore/TabBarController.swift b/AltStore/TabBarController.swift index f1b9daa9..4dddee8d 100644 --- a/AltStore/TabBarController.swift +++ b/AltStore/TabBarController.swift @@ -33,6 +33,7 @@ class TabBarController: UITabBarController NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.openPatreonSettings(_:)), name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.importApp(_:)), name: AppDelegate.importAppDeepLinkNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.presentSources(_:)), name: AppDelegate.addSourceDeepLinkNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.openErrorLog(_:)), name: ToastView.openErrorLogNotification, object: nil) } override func viewDidAppear(_ animated: Bool) @@ -141,4 +142,9 @@ private extension TabBarController { self.selectedIndex = Tab.myApps.rawValue } + + @objc func openErrorLog(_ notification: Notification) + { + self.selectedIndex = Tab.settings.rawValue + } }