diff --git a/Kiwix-iOS/AppDelegate.swift b/Kiwix-iOS/AppDelegate.swift index 530ff24a..b9496d87 100644 --- a/Kiwix-iOS/AppDelegate.swift +++ b/Kiwix-iOS/AppDelegate.swift @@ -22,6 +22,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { URLProtocol.registerClass(KiwixURLProtocol.self) _ = Network.shared + _ = AppNotification.shared return true } diff --git a/Kiwix-iOS/Controller/Bookmark/BookmarkCollectionController.swift b/Kiwix-iOS/Controller/Bookmark/BookmarkCollectionController.swift index ef875da5..2906bfd8 100644 --- a/Kiwix-iOS/Controller/Bookmark/BookmarkCollectionController.swift +++ b/Kiwix-iOS/Controller/Bookmark/BookmarkCollectionController.swift @@ -147,7 +147,7 @@ class BookmarkCollectionController: CoreDataCollectionBaseController, UICollecti collectionView.deselectItem(at: indexPath, animated: true) let article = fetchedResultController.object(at: indexPath) guard let url = article.url else {return} - GlobalQueue.shared.add(articleLoadOperation: ArticleLoadOperation(url: url)) + GlobalQueue.shared.add(articleLoad: ArticleLoadOperation(url: url)) } } diff --git a/Kiwix-iOS/Controller/Library/LibraryTabController.swift b/Kiwix-iOS/Controller/Library/LibraryTabController.swift index 5d9c10a6..dded7c53 100644 --- a/Kiwix-iOS/Controller/Library/LibraryTabController.swift +++ b/Kiwix-iOS/Controller/Library/LibraryTabController.swift @@ -23,6 +23,6 @@ class LibraryTabController: UITabBarController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - AppNotification.shared.register() + AppNotification.shared.requestAuth() } } diff --git a/Kiwix-iOS/Controller/Search/SearchResultController.swift b/Kiwix-iOS/Controller/Search/SearchResultController.swift index 47ab7bba..f54a9828 100644 --- a/Kiwix-iOS/Controller/Search/SearchResultController.swift +++ b/Kiwix-iOS/Controller/Search/SearchResultController.swift @@ -98,8 +98,7 @@ class SearchResultController: SearchBaseTableController, UITableViewDataSource, func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { tableView.deselectRow(at: indexPath, animated: true) let result = searchResults[indexPath.row] - let operation = ArticleLoadOperation(bookID: result.bookID, articleTitle: result.title) - GlobalQueue.shared.add(articleLoadOperation: operation) + GlobalQueue.shared.add(articleLoad: ArticleLoadOperation(bookID: result.bookID, articleTitle: result.title)) Preference.RecentSearch.add(term: searchText) } diff --git a/Kiwix-iOS/Controller/Search/SearchScopeAndHistoryController.swift b/Kiwix-iOS/Controller/Search/SearchScopeAndHistoryController.swift index eb5f0149..dbac83fa 100644 --- a/Kiwix-iOS/Controller/Search/SearchScopeAndHistoryController.swift +++ b/Kiwix-iOS/Controller/Search/SearchScopeAndHistoryController.swift @@ -100,8 +100,7 @@ class SearchScopeAndHistoryController: SearchBaseTableController, UITableViewDel tableView.deselectRow(at: indexPath, animated: true) let book = fetchedResultController.object(at: indexPath) - let operation = ArticleLoadOperation(bookID: book.id) - GlobalQueue.shared.add(articleLoadOperation: operation) + GlobalQueue.shared.add(articleLoad: ArticleLoadOperation(bookID: book.id)) } // MARK: - DZNEmptyDataSet diff --git a/Kiwix-iOS/Notification.swift b/Kiwix-iOS/Notification.swift index 080e62d0..862cefeb 100644 --- a/Kiwix-iOS/Notification.swift +++ b/Kiwix-iOS/Notification.swift @@ -10,12 +10,25 @@ import UserNotifications class AppNotification: NSObject, UNUserNotificationCenterDelegate { static let shared = AppNotification() - private override init() {} + private override init() { + super.init() + UNUserNotificationCenter.current().delegate = self + registerActions() + } - func register() { + let downloadFinishIdentifier = "org.kiwix.download-finished" + + func requestAuth() { UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { _ in } } + func registerActions() { + let downloadFinish = UNNotificationCategory(identifier: downloadFinishIdentifier, actions: [ + UNNotificationAction(identifier: "loadMain", title: "Open Main Page", options: .foreground) + ], intentIdentifiers: []) + UNUserNotificationCenter.current().setNotificationCategories([downloadFinish]) + } + func downloadFinished(bookID: String, bookTitle: String, fileSizeDescription: String) { UNUserNotificationCenter.current().getNotificationSettings(completionHandler: { (settings) in guard settings.alertSetting == .enabled else {return} @@ -23,7 +36,8 @@ class AppNotification: NSObject, UNUserNotificationCenterDelegate { content.categoryIdentifier = "org.kiwix.download-finished" content.title = bookTitle + " is downloaded!" content.body = fileSizeDescription + " has been transferred." - let request = UNNotificationRequest(identifier: "org.kiwix.download-finished." + bookID, content: content, trigger: nil) + content.categoryIdentifier = self.downloadFinishIdentifier + let request = UNNotificationRequest(identifier: [self.downloadFinishIdentifier, bookID].joined(separator: "."), content: content, trigger: nil) UNUserNotificationCenter.current().add(request, withCompletionHandler: nil) }) } @@ -34,5 +48,13 @@ class AppNotification: NSObject, UNUserNotificationCenterDelegate { completionHandler([.alert, .sound]) } - + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + let requestIdentifier = response.notification.request.identifier + if requestIdentifier.hasPrefix(downloadFinishIdentifier) { + let bookID = requestIdentifier.replacingOccurrences(of: downloadFinishIdentifier + ".", with: "") + if response.actionIdentifier == UNNotificationDefaultActionIdentifier || response.actionIdentifier == "loadMain" { + GlobalQueue.shared.add(articleLoad: ArticleLoadOperation(bookID: bookID)) + } + } + } } diff --git a/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index ed9a9b4d..b7203960 100644 --- a/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Kiwix.xcworkspace/xcuserdata/chrisli.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -2,4 +2,38 @@ + + + + + + + + + + diff --git a/Kiwix/Operations/ArticleOperation.swift b/Kiwix/Operations/ArticleOperation.swift index 6d60a2a6..b231c990 100644 --- a/Kiwix/Operations/ArticleOperation.swift +++ b/Kiwix/Operations/ArticleOperation.swift @@ -78,7 +78,10 @@ class ArticleLoadOperation: Procedure { OperationQueue.main.addOperation { _ = main.searchBar.resignFirstResponder() - main.presentedViewController?.dismiss(animated: self.animated, completion: nil) + + main.presentedViewController?.dismiss(animated: true, completion: { + main.presentedViewController?.dismiss(animated: true, completion: nil) + }) main.hideWelcome() if main.traitCollection.horizontalSizeClass == .compact { diff --git a/Kiwix/Operations/Queue.swift b/Kiwix/Operations/Queue.swift index 65681709..891f69a2 100644 --- a/Kiwix/Operations/Queue.swift +++ b/Kiwix/Operations/Queue.swift @@ -32,17 +32,17 @@ class GlobalQueue: ProcedureQueue { } private weak var articleLoadOperation: ArticleLoadOperation? - func add(articleLoadOperation: ArticleLoadOperation) { + func add(articleLoad operation: ArticleLoadOperation) { if let scanOperation = scanOperation { - articleLoadOperation.addDependency(scanOperation) + operation.addDependency(scanOperation) } if let articleLoadOperation = self.articleLoadOperation { articleLoadOperation.addDependency(articleLoadOperation) } - add(operation: articleLoadOperation) - self.articleLoadOperation = articleLoadOperation + add(operation: operation) + self.articleLoadOperation = operation } } diff --git a/Kiwix/Operations/UIProcedure.swift b/Kiwix/Operations/UIProcedure.swift index fc04b285..038bff07 100644 --- a/Kiwix/Operations/UIProcedure.swift +++ b/Kiwix/Operations/UIProcedure.swift @@ -106,7 +106,7 @@ extension AlertProcedure { } else if book.state == .local { alert.add(actionWithTitle: "set back to cloud", style: .default) { _ in let context = AppDelegate.persistentContainer.viewContext - context.perform({ + context.perform({ book.state = .cloud }) alert.finish() @@ -122,31 +122,12 @@ extension AlertProcedure { extension AlertProcedure { class Library { - static func refreshError(context: UIViewController) -> AlertProcedure { + static func refreshError(context: UIViewController, message: String) -> AlertProcedure { assert(Thread.isMainThread, "Library refresh error alert has to be initialized in the main thread") let alert = AlertProcedure(presentAlertFrom: context, withPreferredStyle: .actionSheet, waitForDismissal: true) - alert.title = "There was an error" - if book.state == .cloud { - alert.add(actionWithTitle: Localized.Library.download, style: .default) { _ in - Network.shared.start(bookID: book.id) - alert.finish() - } - alert.add(actionWithTitle: Localized.Library.copyURL, style: .default) { _ in - guard let url = book.url else {return} - UIPasteboard.general.string = url.absoluteString - alert.finish() - } - } else if book.state == .local { - alert.add(actionWithTitle: "set back to cloud", style: .default) { _ in - let context = AppDelegate.persistentContainer.viewContext - context.perform({ - book.state = .cloud - }) - alert.finish() - } - } - - alert.add(actionWithTitle: Localized.Common.cancel, style: .cancel) { _ in alert.finish() } + alert.title = Localized.Library.RefreshError.title + alert.message = message + alert.add(actionWithTitle: Localized.Common.ok, style: .cancel) { _ in alert.finish() } return alert } } diff --git a/Kiwix/Tools/StringTools.swift b/Kiwix/Tools/StringTools.swift index 3e1e30fe..dac1cb55 100644 --- a/Kiwix/Tools/StringTools.swift +++ b/Kiwix/Tools/StringTools.swift @@ -94,6 +94,11 @@ class Localized { static let hiding = NSLocalizedString("HIDING", comment: "Library, Language Filter") static let original = NSLocalizedString("Original", comment: "Library, Language Filter") } + + class RefreshError { + static let title = NSLocalizedString("Unable to refresh library", comment: "Library, Refresh Error") + static let subtitle = NSLocalizedString("Please try again later", comment: "Library, Refresh Error") + } } // MARK: - Setting