diff --git a/AltStore/Managing Apps/AppManager.swift b/AltStore/Managing Apps/AppManager.swift index f04fc60a..3a36ed47 100644 --- a/AltStore/Managing Apps/AppManager.swift +++ b/AltStore/Managing Apps/AppManager.swift @@ -26,6 +26,7 @@ extension AppManager static let didUpdatePatronsNotification = Notification.Name("io.altstore.AppManager.didUpdatePatrons") static let didAddSourceNotification = Notification.Name("io.altstore.AppManager.didAddSource") static let didRemoveSourceNotification = Notification.Name("io.altstore.AppManager.didRemoveSource") + static let willInstallAppFromNewSourceNotification = Notification.Name("io.altstore.AppManager.willInstallAppFromNewSource") static let expirationWarningNotificationID = "altstore-expiration-warning" static let enableJITResultNotificationID = "altstore-enable-jit" @@ -405,6 +406,7 @@ extension AppManager completionHandler: @escaping (Result) -> Void) async -> RefreshGroup { @AsyncManaged var installingApp: AppProtocol = app + var didAddSource = false do { @@ -446,6 +448,8 @@ extension AppManager guard let storeApp = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first else { throw OperationError.appNotFound(name: appName) } return storeApp } + + didAddSource = true } } catch @@ -458,6 +462,15 @@ extension AppManager } let group = await $installingApp.perform { self.install($0, presentingViewController: presentingViewController, context: context, completionHandler: completionHandler) } + + if didAddSource + { + // Post notification from main queue _after_ assigning progress for it + await MainActor.run { [installingApp] in + NotificationCenter.default.post(name: AppManager.willInstallAppFromNewSourceNotification, object: installingApp) + } + } + return group } } diff --git a/AltStore/Sources/SourcesViewController.swift b/AltStore/Sources/SourcesViewController.swift index 6533c9a3..f34cb64f 100644 --- a/AltStore/Sources/SourcesViewController.swift +++ b/AltStore/Sources/SourcesViewController.swift @@ -57,6 +57,8 @@ final class SourcesViewController: UICollectionViewController private lazy var dataSource = self.makeDataSource() + private weak var _installingApp: StoreApp? + private var placeholderView: RSTPlaceholderView! private var placeholderViewButton: UIButton! private var placeholderViewCenterYConstraint: NSLayoutConstraint! @@ -115,6 +117,8 @@ final class SourcesViewController: UICollectionViewController self.navigationItem.rightBarButtonItem = self.editButtonItem + NotificationCenter.default.addObserver(self, selector: #selector(SourcesViewController.showInstallingAppToastView(_:)), name: AppManager.willInstallAppFromNewSourceNotification, object: nil) + self.update() } @@ -512,6 +516,26 @@ private extension SourcesViewController self.editButtonItem.isEnabled = true } } + + @objc func showInstallingAppToastView(_ notification: Notification) + { + guard let app = notification.object as? StoreApp else { return } + self._installingApp = app + + let text = String(format: NSLocalizedString("Downloading %@…", comment: ""), app.name) + let toastView = ToastView(text: text, detailText: NSLocalizedString("Tap to view progress.", comment: "")) + toastView.addTarget(self, action: #selector(SourcesViewController.showAppDetail), for: .touchUpInside) + toastView.show(in: self) + } + + @objc func showAppDetail() + { + guard let app = self._installingApp else { return } + self._installingApp = nil + + let appViewController = AppViewController.makeAppViewController(app: app) + self.navigationController?.pushViewController(appViewController, animated: true) + } } extension SourcesViewController