Persist and restore container expanded state across application launches. Issue #1361
This commit is contained in:
parent
3d1f68a877
commit
4a9e79cd1e
|
@ -16,4 +16,42 @@ public enum ContainerIdentifier: Hashable {
|
||||||
case smartFeedController
|
case smartFeedController
|
||||||
case account(String) // accountID
|
case account(String) // accountID
|
||||||
case folder(String, String) // accountID, folderName
|
case folder(String, String) // accountID, folderName
|
||||||
|
|
||||||
|
public var userInfo: [AnyHashable: Any] {
|
||||||
|
switch self {
|
||||||
|
case .smartFeedController:
|
||||||
|
return [
|
||||||
|
"type": "smartFeedController"
|
||||||
|
]
|
||||||
|
case .account(let accountID):
|
||||||
|
return [
|
||||||
|
"type": "account",
|
||||||
|
"accountID": accountID
|
||||||
|
]
|
||||||
|
case .folder(let accountID, let folderName):
|
||||||
|
return [
|
||||||
|
"type": "folder",
|
||||||
|
"accountID": accountID,
|
||||||
|
"folderName": folderName
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init?(userInfo: [AnyHashable: Any]) {
|
||||||
|
guard let type = userInfo["type"] as? String else { return nil }
|
||||||
|
|
||||||
|
switch type {
|
||||||
|
case "smartFeedController":
|
||||||
|
self = ContainerIdentifier.smartFeedController
|
||||||
|
case "account":
|
||||||
|
guard let accountID = userInfo["accountID"] as? String else { return nil }
|
||||||
|
self = ContainerIdentifier.account(accountID)
|
||||||
|
case "folder":
|
||||||
|
guard let accountID = userInfo["accountID"] as? String, let folderName = userInfo["folderName"] as? String else { return nil }
|
||||||
|
self = ContainerIdentifier.folder(accountID, folderName)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,19 @@ class ActivityManager {
|
||||||
private var readingActivity: NSUserActivity?
|
private var readingActivity: NSUserActivity?
|
||||||
private var readingArticle: Article?
|
private var readingArticle: Article?
|
||||||
|
|
||||||
var stateRestorationActivity: NSUserActivity? {
|
var stateRestorationActivity: NSUserActivity {
|
||||||
if readingActivity != nil {
|
if let activity = readingActivity {
|
||||||
return readingActivity
|
return activity
|
||||||
}
|
}
|
||||||
return selectingActivity
|
|
||||||
|
if let activity = selectingActivity {
|
||||||
|
return activity
|
||||||
|
}
|
||||||
|
|
||||||
|
let activity = NSUserActivity(activityType: ActivityType.restoration.rawValue)
|
||||||
|
activity.persistentIdentifier = UUID().uuidString
|
||||||
|
activity.becomeCurrent()
|
||||||
|
return activity
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ActivityType: String {
|
enum ActivityType: String {
|
||||||
|
case restoration = "Restoration"
|
||||||
case selectFeed = "SelectFeed"
|
case selectFeed = "SelectFeed"
|
||||||
case nextUnread = "NextUnread"
|
case nextUnread = "NextUnread"
|
||||||
case readArticle = "ReadArticle"
|
case readArticle = "ReadArticle"
|
||||||
|
|
|
@ -23,4 +23,7 @@ struct UserInfoKey {
|
||||||
static let articlePath = "articlePath"
|
static let articlePath = "articlePath"
|
||||||
static let feedIdentifier = "feedIdentifier"
|
static let feedIdentifier = "feedIdentifier"
|
||||||
|
|
||||||
|
static let windowState = "windowState"
|
||||||
|
static let containerExpandedWindowState = "containerExpandedWindowState"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -540,8 +540,9 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let indexPath = dataSource.indexPath(for: node) {
|
if let indexPath = dataSource.indexPath(for: node) {
|
||||||
coordinator.selectFeed(indexPath, animated: animated)
|
coordinator.selectFeed(indexPath, animated: animated) {
|
||||||
completion?()
|
completion?()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
<string>Grant permission to save images from the article.</string>
|
<string>Grant permission to save images from the article.</string>
|
||||||
<key>NSUserActivityTypes</key>
|
<key>NSUserActivityTypes</key>
|
||||||
<array>
|
<array>
|
||||||
|
<string>Restoration</string>
|
||||||
<string>AddWebFeedIntent</string>
|
<string>AddWebFeedIntent</string>
|
||||||
<string>NextUnread</string>
|
<string>NextUnread</string>
|
||||||
<string>ReadArticle</string>
|
<string>ReadArticle</string>
|
||||||
|
|
|
@ -101,8 +101,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||||
private let treeControllerDelegate = WebFeedTreeControllerDelegate()
|
private let treeControllerDelegate = WebFeedTreeControllerDelegate()
|
||||||
private let treeController: TreeController
|
private let treeController: TreeController
|
||||||
|
|
||||||
var stateRestorationActivity: NSUserActivity? {
|
var stateRestorationActivity: NSUserActivity {
|
||||||
return activityManager.stateRestorationActivity
|
let activity = activityManager.stateRestorationActivity
|
||||||
|
var userInfo = activity.userInfo == nil ? [AnyHashable: Any]() : activity.userInfo
|
||||||
|
userInfo![UserInfoKey.windowState] = windowState()
|
||||||
|
activity.userInfo = userInfo
|
||||||
|
return activity
|
||||||
}
|
}
|
||||||
|
|
||||||
var isRootSplitCollapsed: Bool {
|
var isRootSplitCollapsed: Bool {
|
||||||
|
@ -315,11 +319,20 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||||
return rootSplitViewController
|
return rootSplitViewController
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restoreWindowState(_ activity: NSUserActivity) {
|
||||||
|
if let windowState = activity.userInfo?[UserInfoKey.windowState] as? [AnyHashable: Any] {
|
||||||
|
restoreWindowState(windowState)
|
||||||
|
rebuildShadowTable()
|
||||||
|
masterFeedViewController.reloadFeeds()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handle(_ activity: NSUserActivity) {
|
func handle(_ activity: NSUserActivity) {
|
||||||
selectFeed(nil, animated: false) {
|
selectFeed(nil, animated: false) {
|
||||||
|
|
||||||
guard let activityType = ActivityType(rawValue: activity.activityType) else { return }
|
guard let activityType = ActivityType(rawValue: activity.activityType) else { return }
|
||||||
switch activityType {
|
switch activityType {
|
||||||
|
case .restoration:
|
||||||
|
break
|
||||||
case .selectFeed:
|
case .selectFeed:
|
||||||
self.handleSelectFeed(activity.userInfo)
|
self.handleSelectFeed(activity.userInfo)
|
||||||
case .nextUnread:
|
case .nextUnread:
|
||||||
|
@ -329,7 +342,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||||
case .addFeedIntent:
|
case .addFeedIntent:
|
||||||
self.showAdd(.feed)
|
self.showAdd(.feed)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1744,6 +1756,20 @@ private extension SceneCoordinator {
|
||||||
|
|
||||||
// MARK: NSUserActivity
|
// MARK: NSUserActivity
|
||||||
|
|
||||||
|
func windowState() -> [AnyHashable: Any] {
|
||||||
|
let containerIdentifierUserInfos = expandedTable.map( { $0.userInfo })
|
||||||
|
return [
|
||||||
|
UserInfoKey.containerExpandedWindowState: containerIdentifierUserInfos
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreWindowState(_ windowState: [AnyHashable: Any]) {
|
||||||
|
if let containerIdentifierUserInfos = windowState[UserInfoKey.containerExpandedWindowState] as? [[AnyHashable: Any]] {
|
||||||
|
let containerIdentifers = containerIdentifierUserInfos.compactMap( { ContainerIdentifier(userInfo: $0) })
|
||||||
|
expandedTable = Set(containerIdentifers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func handleSelectFeed(_ userInfo: [AnyHashable : Any]?) {
|
func handleSelectFeed(_ userInfo: [AnyHashable : Any]?) {
|
||||||
guard let userInfo = userInfo,
|
guard let userInfo = userInfo,
|
||||||
let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : Any],
|
let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : Any],
|
||||||
|
|
|
@ -23,6 +23,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
window!.tintColor = AppAssets.primaryAccentColor
|
window!.tintColor = AppAssets.primaryAccentColor
|
||||||
window!.rootViewController = coordinator.start(for: window!.frame.size)
|
window!.rootViewController = coordinator.start(for: window!.frame.size)
|
||||||
|
|
||||||
|
if let stateRestorationActivity = session.stateRestorationActivity {
|
||||||
|
coordinator.restoreWindowState(stateRestorationActivity)
|
||||||
|
}
|
||||||
|
|
||||||
if let shortcutItem = connectionOptions.shortcutItem {
|
if let shortcutItem = connectionOptions.shortcutItem {
|
||||||
window!.makeKeyAndVisible()
|
window!.makeKeyAndVisible()
|
||||||
handleShortcutItem(shortcutItem)
|
handleShortcutItem(shortcutItem)
|
||||||
|
|
Loading…
Reference in New Issue