Add user activities for read all/unread

This commit is contained in:
Shadowfacts 2022-03-07 21:54:41 -05:00
parent 7f5006c629
commit 2f6d0ae07c
5 changed files with 111 additions and 40 deletions

View File

@ -4,6 +4,8 @@
<dict> <dict>
<key>NSUserActivityTypes</key> <key>NSUserActivityTypes</key>
<array> <array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.read-unread</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.read-all</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.preferences</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.preferences</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.add-account</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.add-account</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.activate-account</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER).activity.activate-account</string>

View File

@ -5,7 +5,7 @@
// Created by Shadowfacts on 10/29/21. // Created by Shadowfacts on 10/29/21.
// //
import Foundation @preconcurrency import Foundation
import UIKit import UIKit
import OSLog import OSLog
@ -27,30 +27,31 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window = UIWindow(windowScene: windowScene) window = UIWindow(windowScene: windowScene)
window!.tintColor = .appTintColor window!.tintColor = .appTintColor
let activity = connectionOptions.userActivities.first var activity = connectionOptions.userActivities.first
var account = LocalData.mostRecentAccount()
if activity?.activityType == NSUserActivity.addAccountType { if activity?.activityType == NSUserActivity.addAccountType {
let loginVC = LoginViewController() account = nil
loginVC.delegate = self } else if let id = activity?.accountID() {
window!.rootViewController = loginVC account = LocalData.account(with: id)
} else if activity?.activityType == NSUserActivity.activateAccountType, if account == nil {
let idStr = activity!.userInfo?["accountID"] as? String, activity = nil
let id = Data(base64Encoded: idStr), logger.log("Missing account for activity, not restoring")
let account = LocalData.account(with: id) {
Task { @MainActor in
fervorController = await FervorController(account: account)
syncFromServer()
createAppUI()
} }
} else if let account = LocalData.mostRecentAccount() { }
Task { @MainActor in
if let account = account {
Task { @MainActor [activity] in
fervorController = await FervorController(account: account) fervorController = await FervorController(account: account)
syncFromServer() syncFromServer()
createAppUI() createAppUI()
if let activity = activity {
setupUI(from: activity)
}
} }
} else { } else {
let loginVC = LoginViewController() createLoginUI()
loginVC.delegate = self
window!.rootViewController = loginVC
} }
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
@ -106,6 +107,31 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
// to restore the scene back to its current state. // to restore the scene back to its current state.
} }
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
setupUI(from: userActivity)
}
private func setupUI(from activity: NSUserActivity) {
guard let split = window?.rootViewController as? AppSplitViewController else {
logger.error("Failed to setup UI for user activity: missing split VC")
return
}
switch activity.activityType {
case NSUserActivity.readUnreadType:
split.selectHomeItem(.unread)
case NSUserActivity.readAllType:
split.selectHomeItem(.all)
default:
break
}
}
private func createLoginUI() {
let vc = LoginViewController()
vc.delegate = self
window!.rootViewController = vc
}
private func createAppUI() { private func createAppUI() {
window!.rootViewController = AppSplitViewController(fervorController: fervorController) window!.rootViewController = AppSplitViewController(fervorController: fervorController)
} }

View File

@ -53,6 +53,18 @@ class AppSplitViewController: UISplitViewController {
let nav = AppNavigationController(rootViewController: home) let nav = AppNavigationController(rootViewController: home)
setViewController(nav, for: .compact) setViewController(nav, for: .compact)
} }
func selectHomeItem(_ item: HomeViewController.Item) {
let column: Column
if traitCollection.horizontalSizeClass == .compact {
column = .compact
} else {
column = .primary
}
let nav = viewController(for: column) as! UINavigationController
let home = nav.viewControllers.first! as! HomeViewController
home.selectItem(item)
}
} }

View File

@ -198,6 +198,28 @@ class HomeViewController: UIViewController {
} }
} }
private func itemsViewController(for item: Item) -> ItemsViewController {
let vc = ItemsViewController(fetchRequest: item.idFetchRequest, fervorController: fervorController)
vc.title = item.title
vc.delegate = itemsDelegate
switch item {
case .all:
vc.userActivity = .readAll()
case .unread:
vc.userActivity = .readUnread()
case .group(let group):
break
case .feed(let feed):
break
}
return vc
}
func selectItem(_ item: Item) {
navigationController!.popToRootViewController(animated: false)
navigationController!.pushViewController(itemsViewController(for: item), animated: false)
}
} }
extension HomeViewController { extension HomeViewController {
@ -236,21 +258,6 @@ extension HomeViewController {
} }
} }
var fetchRequest: NSFetchRequest<Reader.Item> {
let req = Reader.Item.fetchRequest()
switch self {
case .unread:
req.predicate = NSPredicate(format: "read = NO")
case .all:
break
case .group(let group):
req.predicate = NSPredicate(format: "feed in %@", group.feeds!)
case .feed(let feed):
req.predicate = NSPredicate(format: "feed = %@", feed)
}
return req
}
var idFetchRequest: NSFetchRequest<NSManagedObjectID> { var idFetchRequest: NSFetchRequest<NSManagedObjectID> {
let req = NSFetchRequest<NSManagedObjectID>(entityName: "Item") let req = NSFetchRequest<NSManagedObjectID>(entityName: "Item")
req.resultType = .managedObjectIDResultType req.resultType = .managedObjectIDResultType
@ -307,10 +314,7 @@ extension HomeViewController: UICollectionViewDelegate {
guard let item = dataSource.itemIdentifier(for: indexPath) else { guard let item = dataSource.itemIdentifier(for: indexPath) else {
return return
} }
let vc = ItemsViewController(fetchRequest: item.idFetchRequest, fervorController: fervorController) show(itemsViewController(for: item), sender: nil)
vc.title = item.title
vc.delegate = itemsDelegate
show(vc, sender: nil)
UISelectionFeedbackGenerator().selectionChanged() UISelectionFeedbackGenerator().selectionChanged()
} }
@ -318,8 +322,8 @@ extension HomeViewController: UICollectionViewDelegate {
guard let item = dataSource.itemIdentifier(for: indexPath) else { guard let item = dataSource.itemIdentifier(for: indexPath) else {
return nil return nil
} }
return UIContextMenuConfiguration(identifier: nil, previewProvider: { return UIContextMenuConfiguration(identifier: nil, previewProvider: { [unowned self] in
return ItemsViewController(fetchRequest: item.idFetchRequest, fervorController: self.fervorController) return self.itemsViewController(for: item)
}, actionProvider: nil) }, actionProvider: nil)
} }

View File

@ -12,6 +12,17 @@ extension NSUserActivity {
static let preferencesType = "net.shadowfacts.Reader.activity.preferences" static let preferencesType = "net.shadowfacts.Reader.activity.preferences"
static let addAccountType = "net.shadowfacts.Reader.activity.add-account" static let addAccountType = "net.shadowfacts.Reader.activity.add-account"
static let activateAccountType = "net.shadowfacts.Reader.activity.activate-account" static let activateAccountType = "net.shadowfacts.Reader.activity.activate-account"
static let readUnreadType = "net.shadowfacts.Reader.activity.read-unread"
static let readAllType = "net.shadowfacts.Reader.activity.read-all"
func accountID() -> Data? {
if [NSUserActivity.addAccountType].contains(self.activityType),
let id = self.userInfo?["accountID"] as? Data {
return id
} else {
return nil
}
}
static func preferences() -> NSUserActivity { static func preferences() -> NSUserActivity {
return NSUserActivity(activityType: preferencesType) return NSUserActivity(activityType: preferencesType)
@ -24,9 +35,25 @@ extension NSUserActivity {
static func activateAccount(_ account: LocalData.Account) -> NSUserActivity { static func activateAccount(_ account: LocalData.Account) -> NSUserActivity {
let activity = NSUserActivity(activityType: activateAccountType) let activity = NSUserActivity(activityType: activateAccountType)
activity.userInfo = [ activity.userInfo = [
"accountID": account.id.uuidString "accountID": account.id,
] ]
return activity return activity
} }
static func readUnread() -> NSUserActivity {
let activity = NSUserActivity(activityType: readUnreadType)
activity.isEligibleForHandoff = true
activity.isEligibleForPrediction = true
activity.title = "Show unread articles"
return activity
}
static func readAll() -> NSUserActivity {
let activity = NSUserActivity(activityType: readAllType)
activity.isEligibleForHandoff = true
activity.isEligibleForPrediction = true
activity.title = "Show all articles"
return activity
}
} }