Merge pull request #4174 from vincode-io/main

Fix state restoration when compiling against the macOS 14 SDK
This commit is contained in:
Brent Simmons
2023-12-16 17:50:38 -08:00
committed by GitHub
14 changed files with 287 additions and 119 deletions

View File

@@ -76,10 +76,10 @@ final class AppDefaults {
firstRunDate = Date()
return true
}()
var windowState: [AnyHashable : Any]? {
var windowState: Data? {
get {
return UserDefaults.standard.object(forKey: Key.windowState) as? [AnyHashable : Any]
return UserDefaults.standard.object(forKey: Key.windowState) as? Data
}
set {
UserDefaults.standard.set(newValue, forKey: Key.windowState)

View File

@@ -308,6 +308,10 @@ var appDelegate: AppDelegate!
return false
}
func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
return true
}
func applicationDidBecomeActive(_ notification: Notification) {
fireOldTimers()
}

View File

@@ -25,6 +25,10 @@ enum DetailState: Equatable {
@IBOutlet var containerView: DetailContainerView!
@IBOutlet var statusBarView: DetailStatusBarView!
var windowState: DetailWindowState {
return currentWebViewController.windowState
}
lazy var regularWebViewController = {
return createWebViewController()
}()
@@ -89,12 +93,6 @@ enum DetailState: Equatable {
window.makeFirstResponderUnlessDescendantIsFirstResponder(currentWebViewController.webView)
}
// MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) {
currentWebViewController.saveState(to: &state)
}
// MARK: Find in Article
private var didLoadTextFinder = false

View File

@@ -35,6 +35,10 @@ protocol DetailWebViewControllerDelegate: AnyObject {
}
}
var windowState: DetailWindowState {
return DetailWindowState(isShowingExtractedArticle: isShowingExtractedArticle, windowScrollY: windowScrollY ?? 0)
}
var article: Article? {
switch state {
case .article(let article, _):
@@ -191,13 +195,6 @@ protocol DetailWebViewControllerDelegate: AnyObject {
webView.scrollPageUp(sender)
}
// MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) {
state[UserInfoKey.isShowingExtractedArticle] = isShowingExtractedArticle
state[UserInfoKey.articleWindowScrollY] = windowScrollY
}
// MARK: Find in Article
var canFindInArticle: Bool {

View File

@@ -0,0 +1,33 @@
//
// DetailWindowState.swift
// NetNewsWire
//
// Created by Maurice Parker on 12/16/23.
// Copyright © 2023 Ranchero Software. All rights reserved.
//
import Foundation
class DetailWindowState: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
let isShowingExtractedArticle: Bool
let windowScrollY: CGFloat
internal init(isShowingExtractedArticle: Bool, windowScrollY: CGFloat) {
self.isShowingExtractedArticle = isShowingExtractedArticle
self.windowScrollY = windowScrollY
}
required init?(coder: NSCoder) {
isShowingExtractedArticle = coder.decodeBool(forKey: "isShowingExtractedArticle")
windowScrollY = coder.decodeObject(of: NSNumber.self, forKey: "windowScrollY") as? CGFloat ?? 0
}
func encode(with coder: NSCoder) {
coder.encode(isShowingExtractedArticle, forKey: "isShowingExtractedArticle")
coder.encode(windowScrollY, forKey: "windowScrollY")
}
}

View File

@@ -133,12 +133,14 @@ enum TimelineSourceMode {
}
func saveStateToUserDefaults() {
AppDefaults.shared.windowState = savableState()
let data = try? NSKeyedArchiver.archivedData(withRootObject: savableState(), requiringSecureCoding: true)
AppDefaults.shared.windowState = data
window?.saveFrame(usingName: windowAutosaveName)
}
func restoreStateFromUserDefaults() {
if let state = AppDefaults.shared.windowState {
if let data = AppDefaults.shared.windowState,
let state = try? NSKeyedUnarchiver.unarchivedObject(ofClass: MainWindowState.self, from: data) {
restoreState(from: state)
window?.setFrameUsingName(windowAutosaveName, force: true)
}
@@ -630,7 +632,7 @@ extension MainWindowController: NSWindowDelegate {
}
func window(_ window: NSWindow, didDecodeRestorableState coder: NSCoder) {
guard let state = try? coder.decodeTopLevelObject(forKey: UserInfoKey.windowState) as? [AnyHashable : Any] else { return }
guard let state = coder.decodeObject(of: MainWindowState.self, forKey: UserInfoKey.windowState) else { return }
restoreState(from: state)
}
@@ -1087,31 +1089,39 @@ private extension MainWindowController {
// MARK: - State Restoration
func savableState() -> [AnyHashable : Any] {
var state = [AnyHashable : Any]()
state[UserInfoKey.windowFullScreenState] = window?.styleMask.contains(.fullScreen) ?? false
saveSplitViewState(to: &state)
sidebarViewController?.saveState(to: &state)
timelineContainerViewController?.saveState(to: &state)
detailViewController?.saveState(to: &state)
return state
func savableState() -> MainWindowState {
let isFullScreen = window?.styleMask.contains(.fullScreen) ?? false
let splitViewWidths: [Int]
if let splitView = splitViewController?.splitView {
splitViewWidths = splitView.arrangedSubviews.map{ Int(floor($0.frame.width)) }
} else {
splitViewWidths = []
}
let isSidebarHidden = sidebarSplitViewItem?.isCollapsed ?? false
return MainWindowState(isFullScreen: isFullScreen,
splitViewWidths: splitViewWidths,
isSidebarHidden: isSidebarHidden,
sidebarWindowState: sidebarViewController?.windowState,
timelineWindowState: timelineContainerViewController?.windowState,
detailWindowState: detailViewController?.windowState)
}
func restoreState(from state: [AnyHashable : Any]) {
if let fullScreen = state[UserInfoKey.windowFullScreenState] as? Bool, fullScreen {
func restoreState(from state: MainWindowState) {
if state.isFullScreen {
window?.toggleFullScreen(self)
}
restoreSplitViewState(from: state)
sidebarViewController?.restoreState(from: state)
sidebarViewController?.restoreState(from: state.sidebarWindowState)
let articleWindowScrollY = state[UserInfoKey.articleWindowScrollY] as? CGFloat
restoreArticleWindowScrollY = articleWindowScrollY
timelineContainerViewController?.restoreState(from: state)
let isShowingExtractedArticle = state[UserInfoKey.isShowingExtractedArticle] as? Bool ?? false
timelineContainerViewController?.restoreState(from: state.timelineWindowState)
restoreArticleWindowScrollY = state.detailWindowState?.windowScrollY
let isShowingExtractedArticle = state.detailWindowState?.isShowingExtractedArticle as? Bool ?? false
if isShowingExtractedArticle {
restoreArticleWindowScrollY = articleWindowScrollY
startArticleExtractorForCurrentLink()
}
@@ -1379,29 +1389,17 @@ private extension MainWindowController {
}
}
func saveSplitViewState(to state: inout [AnyHashable : Any]) {
guard let splitView = splitViewController?.splitView else {
return
}
let widths = splitView.arrangedSubviews.map{ Int(floor($0.frame.width)) }
state[MainWindowController.mainWindowWidthsStateKey] = widths
state[UserInfoKey.isSidebarHidden] = sidebarSplitViewItem?.isCollapsed
}
func restoreSplitViewState(from state: [AnyHashable : Any]) {
func restoreSplitViewState(from state: MainWindowState) {
guard let splitView = splitViewController?.splitView,
let widths = state[MainWindowController.mainWindowWidthsStateKey] as? [Int],
widths.count == 3,
let window = window else {
return
state.splitViewWidths.count == 3,
let window = window else {
return
}
let windowWidth = Int(floor(window.frame.width))
let dividerThickness: Int = Int(splitView.dividerThickness)
let sidebarWidth: Int = widths[0]
let timelineWidth: Int = widths[1]
let sidebarWidth: Int = state.splitViewWidths[0]
let timelineWidth: Int = state.splitViewWidths[1]
// Make sure the detail view has its minimum thickness, at least.
if windowWidth < sidebarWidth + dividerThickness + timelineWidth + dividerThickness + MainWindowController.detailViewMinimumThickness {
@@ -1410,11 +1408,9 @@ private extension MainWindowController {
splitView.setPosition(CGFloat(sidebarWidth), ofDividerAt: 0)
splitView.setPosition(CGFloat(sidebarWidth + dividerThickness + timelineWidth), ofDividerAt: 1)
let isSidebarHidden = state[UserInfoKey.isSidebarHidden] as? Bool ?? false
if !(sidebarSplitViewItem?.isCollapsed ?? false) && isSidebarHidden {
sidebarSplitViewItem?.isCollapsed = true
Task { @MainActor in
sidebarSplitViewItem?.isCollapsed = state.isSidebarHidden
}
}

View File

@@ -0,0 +1,50 @@
//
// MainWindowState.swift
// NetNewsWire
//
// Created by Maurice Parker on 12/16/23.
// Copyright © 2023 Ranchero Software. All rights reserved.
//
import Foundation
class MainWindowState: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
let isFullScreen: Bool
let splitViewWidths: [Int]
let isSidebarHidden: Bool
let sidebarWindowState: SidebarWindowState?
let timelineWindowState: TimelineWindowState?
let detailWindowState: DetailWindowState?
init(isFullScreen: Bool, splitViewWidths: [Int], isSidebarHidden: Bool, sidebarWindowState: SidebarWindowState? = nil, timelineWindowState: TimelineWindowState? = nil, detailWindowState: DetailWindowState? = nil) {
self.isFullScreen = isFullScreen
self.splitViewWidths = splitViewWidths
self.isSidebarHidden = isSidebarHidden
self.sidebarWindowState = sidebarWindowState
self.timelineWindowState = timelineWindowState
self.detailWindowState = detailWindowState
}
required init?(coder: NSCoder) {
isFullScreen = coder.decodeBool(forKey: "isFullScreen")
splitViewWidths = coder.decodeObject(of: [NSArray.self, NSNumber.self], forKey: "splitViewWidths") as? [Int] ?? []
isSidebarHidden = coder.decodeBool(forKey: "isSidebarHidden")
sidebarWindowState = coder.decodeObject(of: SidebarWindowState.self, forKey: "sidebarWindowState")
timelineWindowState = coder.decodeObject(of: TimelineWindowState.self, forKey: "timelineWindowState")
detailWindowState = coder.decodeObject(of: DetailWindowState.self, forKey: "detailWindowState")
}
func encode(with coder: NSCoder) {
coder.encode(isFullScreen, forKey: "isFullScreen")
coder.encode(splitViewWidths, forKey: "splitViewWidths")
coder.encode(isSidebarHidden, forKey: "isSidebarHidden")
coder.encode(sidebarWindowState, forKey: "sidebarWindowState")
coder.encode(timelineWindowState, forKey: "timelineWindowState")
coder.encode(detailWindowState, forKey: "detailWindowState")
}
}

View File

@@ -31,6 +31,12 @@ protocol SidebarDelegate: AnyObject {
weak var splitViewItem: NSSplitViewItem?
var windowState: SidebarWindowState {
let expandedContainers = expandedTable.compactMap { $0.userInfo as? [String: String] }
let selectedFeeds = selectedFeeds.compactMap { $0.itemID?.userInfo as? [String: String] }
return SidebarWindowState(isReadFiltered: isReadFiltered, expandedContainers: expandedContainers, selectedFeeds: selectedFeeds)
}
private let rebuildTreeAndRestoreSelectionQueue = CoalescingQueue(name: "Rebuild Tree Queue", interval: 1.0)
let treeControllerDelegate = FeedTreeControllerDelegate()
lazy var treeController: TreeController = {
@@ -97,27 +103,14 @@ protocol SidebarDelegate: AnyObject {
// MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) {
state[UserInfoKey.readFeedsFilterState] = isReadFiltered
state[UserInfoKey.containerExpandedWindowState] = expandedTable.map { $0.userInfo }
state[UserInfoKey.selectedFeedsState] = selectedFeeds.compactMap { $0.itemID?.userInfo }
}
func restoreState(from state: [AnyHashable : Any]) {
func restoreState(from state: SidebarWindowState?) {
guard let state else { return }
if let containerExpandedWindowState = state[UserInfoKey.containerExpandedWindowState] as? [[AnyHashable: AnyHashable]] {
let containerIdentifers = containerExpandedWindowState.compactMap( { ContainerIdentifier(userInfo: $0) })
expandedTable = Set(containerIdentifers)
}
let containerIdentifers = state.expandedContainers.compactMap( { ContainerIdentifier(userInfo: $0) })
expandedTable = Set(containerIdentifers)
guard let selectedFeedsState = state[UserInfoKey.selectedFeedsState] as? [[AnyHashable: AnyHashable]] else {
return
}
let selectedItemIdentifers = Set(selectedFeedsState.compactMap( { ItemIdentifier(userInfo: $0) }))
for selectedItemIdentifier in selectedItemIdentifers {
treeControllerDelegate.addFilterException(selectedItemIdentifier)
}
let selectedItemIdentifers = Set(state.selectedFeeds.compactMap( { ItemIdentifier(userInfo: $0) }))
selectedItemIdentifers.forEach { treeControllerDelegate.addFilterException($0) }
rebuildTreeAndReloadDataIfNeeded()
@@ -135,9 +128,7 @@ protocol SidebarDelegate: AnyObject {
outlineView.selectRowIndexes(selectIndexes, byExtendingSelection: false)
focus()
if let readFeedsFilterState = state[UserInfoKey.readFeedsFilterState] as? Bool {
isReadFiltered = readFeedsFilterState
}
isReadFiltered = state.isReadFiltered
}
// MARK: - Notifications

View File

@@ -0,0 +1,37 @@
//
// SidebarWindowState.swift
// NetNewsWire
//
// Created by Maurice Parker on 12/16/23.
// Copyright © 2023 Ranchero Software. All rights reserved.
//
import Foundation
class SidebarWindowState: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
let isReadFiltered: Bool
let expandedContainers: [[String: String]]
let selectedFeeds: [[String: String]]
init(isReadFiltered: Bool, expandedContainers: [[String : String]], selectedFeeds: [[String : String]]) {
self.isReadFiltered = isReadFiltered
self.expandedContainers = expandedContainers
self.selectedFeeds = selectedFeeds
}
required init?(coder: NSCoder) {
isReadFiltered = coder.decodeBool(forKey: "isReadFiltered")
expandedContainers = coder.decodeObject(of: [NSArray.self, NSDictionary.self, NSString.self], forKey: "expandedContainers") as? [[String: String]] ?? []
selectedFeeds = coder.decodeObject(of: [NSArray.self, NSDictionary.self, NSString.self], forKey: "selectedFeeds") as? [[String: String]] ?? []
}
func encode(with coder: NSCoder) {
coder.encode(isReadFiltered, forKey: "isReadFiltered")
coder.encode(expandedContainers, forKey: "expandedContainers")
coder.encode(selectedFeeds, forKey: "selectedFeeds")
}
}

View File

@@ -37,6 +37,10 @@ protocol TimelineContainerViewControllerDelegate: AnyObject {
view?.window?.recalculateKeyViewLoop()
}
}
var windowState: TimelineWindowState? {
return currentTimelineViewController?.windowState
}
weak var delegate: TimelineContainerViewControllerDelegate?
@@ -125,11 +129,9 @@ protocol TimelineContainerViewControllerDelegate: AnyObject {
// MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) {
regularTimelineViewController.saveState(to: &state)
}
func restoreState(from state: [AnyHashable : Any]) {
func restoreState(from state: TimelineWindowState?) {
guard let state else { return }
regularTimelineViewController.restoreState(from: state)
updateReadFilterButton()
}

View File

@@ -79,6 +79,24 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
}
}
var windowState: TimelineWindowState {
let readArticlesFilterStateKeys = readFilterEnabledTable.keys.compactMap { $0.userInfo as? [String: String] }
let readArticlesFilterStateValues = readFilterEnabledTable.values.compactMap( { $0 })
if selectedArticles.count == 1 {
let path = selectedArticles.first!.pathUserInfo
return TimelineWindowState(readArticlesFilterStateKeys: readArticlesFilterStateKeys,
readArticlesFilterStateValues: readArticlesFilterStateValues,
selectedAccountID: path[ArticlePathKey.accountID] as? String,
selectedArticleID: path[ArticlePathKey.articleID] as? String)
} else {
return TimelineWindowState(readArticlesFilterStateKeys: readArticlesFilterStateKeys,
readArticlesFilterStateValues: readArticlesFilterStateValues,
selectedAccountID: nil,
selectedArticleID: nil)
}
}
weak var delegate: TimelineDelegate?
var sharingServiceDelegate: NSSharingServiceDelegate?
@@ -289,36 +307,21 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
// MARK: State Restoration
func saveState(to state: inout [AnyHashable : Any]) {
state[UserInfoKey.readArticlesFilterStateKeys] = readFilterEnabledTable.keys.compactMap { $0.userInfo }
state[UserInfoKey.readArticlesFilterStateValues] = readFilterEnabledTable.values.compactMap( { $0 })
if selectedArticles.count == 1 {
state[UserInfoKey.articlePath] = selectedArticles.first!.pathUserInfo
}
}
func restoreState(from state: [AnyHashable : Any]) {
guard let readArticlesFilterStateKeys = state[UserInfoKey.readArticlesFilterStateKeys] as? [[AnyHashable: AnyHashable]],
let readArticlesFilterStateValues = state[UserInfoKey.readArticlesFilterStateValues] as? [Bool] else {
return
}
for i in 0..<readArticlesFilterStateKeys.count {
if let itemIdentifier = ItemIdentifier(userInfo: readArticlesFilterStateKeys[i]) {
readFilterEnabledTable[itemIdentifier] = readArticlesFilterStateValues[i]
func restoreState(from state: TimelineWindowState) {
for i in 0..<state.readArticlesFilterStateKeys.count {
if let itemIdentifier = ItemIdentifier(userInfo: state.readArticlesFilterStateKeys[i]) {
readFilterEnabledTable[itemIdentifier] = state.readArticlesFilterStateValues[i]
}
}
if let articlePathUserInfo = state[UserInfoKey.articlePath] as? [AnyHashable : Any],
let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String,
let account = AccountManager.shared.existingAccount(with: accountID),
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String {
if let selectedAccountID = state.selectedAccountID,
let account = AccountManager.shared.existingAccount(with: selectedAccountID),
let selectedArticleID = state.selectedArticleID {
exceptionArticleFetcher = SingleArticleFetcher(account: account, articleID: articleID)
exceptionArticleFetcher = SingleArticleFetcher(account: account, articleID: selectedArticleID)
fetchAndReplaceArticlesSync()
if let selectedIndex = articles.firstIndex(where: { $0.articleID == articleID }) {
if let selectedIndex = articles.firstIndex(where: { $0.articleID == selectedArticleID }) {
tableView.selectRow(selectedIndex)
Task { @MainActor in
self.tableView.scrollTo(row: selectedIndex)

View File

@@ -0,0 +1,41 @@
//
// TimelineWindowState.swift
// NetNewsWire
//
// Created by Maurice Parker on 12/16/23.
// Copyright © 2023 Ranchero Software. All rights reserved.
//
import Foundation
class TimelineWindowState: NSObject, NSSecureCoding {
static var supportsSecureCoding = true
let readArticlesFilterStateKeys: [[String: String]]
let readArticlesFilterStateValues: [Bool]
let selectedAccountID: String?
let selectedArticleID: String?
init(readArticlesFilterStateKeys: [[String : String]], readArticlesFilterStateValues: [Bool], selectedAccountID: String? = nil, selectedArticleID: String? = nil) {
self.readArticlesFilterStateKeys = readArticlesFilterStateKeys
self.readArticlesFilterStateValues = readArticlesFilterStateValues
self.selectedAccountID = selectedAccountID
self.selectedArticleID = selectedArticleID
}
required init?(coder: NSCoder) {
readArticlesFilterStateKeys = coder.decodeObject(of: [NSArray.self, NSDictionary.self, NSString.self], forKey: "readArticlesFilterStateKeys") as? [[String: String]] ?? []
readArticlesFilterStateValues = coder.decodeObject(of: [NSArray.self, NSNumber.self], forKey: "readArticlesFilterStateValues") as? [Bool] ?? []
selectedAccountID = coder.decodeObject(of: NSString.self, forKey: "selectedAccountID") as? String
selectedArticleID = coder.decodeObject(of: NSString.self, forKey: "selectedArticleID") as? String
}
func encode(with coder: NSCoder) {
coder.encode(readArticlesFilterStateKeys, forKey: "readArticlesFilterStateKeys")
coder.encode(readArticlesFilterStateValues, forKey: "readArticlesFilterStateValues")
coder.encode(selectedAccountID, forKey: "selectedAccountID")
coder.encode(selectedArticleID, forKey: "selectedArticleID")
}
}

View File

@@ -92,6 +92,14 @@
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
5117715524E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */; };
5117715624E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */; };
5117C1F72B2DF37800A30EAB /* DetailWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1F62B2DF37800A30EAB /* DetailWindowState.swift */; };
5117C1F82B2DF37800A30EAB /* DetailWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1F62B2DF37800A30EAB /* DetailWindowState.swift */; };
5117C1FA2B2DF3B100A30EAB /* TimelineWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1F92B2DF3B100A30EAB /* TimelineWindowState.swift */; };
5117C1FB2B2DF3B100A30EAB /* TimelineWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1F92B2DF3B100A30EAB /* TimelineWindowState.swift */; };
5117C1FD2B2DF3E900A30EAB /* SidebarWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1FC2B2DF3E900A30EAB /* SidebarWindowState.swift */; };
5117C1FE2B2DF3E900A30EAB /* SidebarWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1FC2B2DF3E900A30EAB /* SidebarWindowState.swift */; };
5117C2002B2DF41B00A30EAB /* MainWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1FF2B2DF41B00A30EAB /* MainWindowState.swift */; };
5117C2012B2DF41B00A30EAB /* MainWindowState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5117C1FF2B2DF41B00A30EAB /* MainWindowState.swift */; };
511B148924E5DBDD00C919BD /* Account in Frameworks */ = {isa = PBXBuildFile; productRef = 511B148824E5DBDD00C919BD /* Account */; };
511B149824E5DC2300C919BD /* ShareDefaultContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C8BC23F3780900032075 /* ShareDefaultContainer.swift */; };
511B149924E5DC3D00C919BD /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E46C7C1F75EF7B005ECFB3 /* AppDefaults.swift */; };
@@ -1161,6 +1169,10 @@
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-Extensions.swift"; sourceTree = "<group>"; };
5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
5117C1F62B2DF37800A30EAB /* DetailWindowState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailWindowState.swift; sourceTree = "<group>"; };
5117C1F92B2DF3B100A30EAB /* TimelineWindowState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineWindowState.swift; sourceTree = "<group>"; };
5117C1FC2B2DF3E900A30EAB /* SidebarWindowState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarWindowState.swift; sourceTree = "<group>"; };
5117C1FF2B2DF41B00A30EAB /* MainWindowState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindowState.swift; sourceTree = "<group>"; };
511B9805237DCAC90028BCAA /* UserInfoKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoKey.swift; sourceTree = "<group>"; };
511D43EE231FBDE800FB1562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreenPad.storyboard; sourceTree = "<group>"; };
511D4410231FC02D00FB1562 /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = "<group>"; };
@@ -2263,6 +2275,7 @@
DFC14F0928EA51AB00F6EE86 /* About */,
8483630C2262A3FE00DA1D35 /* MainWindow.storyboard */,
51927A0328E28D1C000AE856 /* MainWindow.swift */,
5117C1FF2B2DF41B00A30EAB /* MainWindowState.swift */,
519279FD28E24CCA000AE856 /* MainWindowController.swift */,
519B8D322143397200FA689C /* SharingServiceDelegate.swift */,
849EE72020391F560082A1EA /* SharingServicePickerDelegate.swift */,
@@ -2421,6 +2434,7 @@
84AD1EBB2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift */,
849A97601ED9EB96007D329B /* SidebarOutlineView.swift */,
849A97821ED9EC63007D329B /* SidebarStatusBarView.swift */,
5117C1FC2B2DF3E900A30EAB /* SidebarWindowState.swift */,
849A97621ED9EB96007D329B /* SidebarViewController.swift */,
84B7178B201E66580091657D /* SidebarViewController+ContextualMenus.swift */,
849A97631ED9EB96007D329B /* UnreadCountView.swift */,
@@ -2437,6 +2451,7 @@
8405DDA422168C62008CE1BF /* TimelineContainerViewController.swift */,
8405DD9822153B6B008CE1BF /* TimelineContainerView.swift */,
8405DDA122168920008CE1BF /* TimelineTableView.xib */,
5117C1F92B2DF3B100A30EAB /* TimelineWindowState.swift */,
849A976B1ED9EBC8007D329B /* TimelineViewController.swift */,
84E8E0DA202EC49300562D8F /* TimelineViewController+ContextualMenus.swift */,
849A97691ED9EBC8007D329B /* TimelineTableRowView.swift */,
@@ -2465,6 +2480,7 @@
849A977C1ED9EC42007D329B /* Detail */ = {
isa = PBXGroup;
children = (
5117C1F62B2DF37800A30EAB /* DetailWindowState.swift */,
849A977E1ED9EC42007D329B /* DetailViewController.swift */,
8405DD892213E0E3008CE1BF /* DetailContainerView.swift */,
84216D0222128B9D0049B9B9 /* DetailWebViewController.swift */,
@@ -3942,6 +3958,7 @@
65ED3FD0235DEF6C0081F399 /* Author+Scriptability.swift in Sources */,
65ED3FD1235DEF6C0081F399 /* PseudoFeed.swift in Sources */,
65ED3FD3235DEF6C0081F399 /* NSScriptCommand+NetNewsWire.swift in Sources */,
5117C2012B2DF41B00A30EAB /* MainWindowState.swift in Sources */,
B2C12C6728F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift in Sources */,
65ED3FD4235DEF6C0081F399 /* Article+Scriptability.swift in Sources */,
65ED3FD5235DEF6C0081F399 /* SmartFeed.swift in Sources */,
@@ -3985,6 +4002,7 @@
65ED3FF3235DEF6C0081F399 /* ArticleSorter.swift in Sources */,
65ED3FF4235DEF6C0081F399 /* TimelineViewController+ContextualMenus.swift in Sources */,
65ED3FF5235DEF6C0081F399 /* ArticleStringFormatter.swift in Sources */,
5117C1FB2B2DF3B100A30EAB /* TimelineWindowState.swift in Sources */,
65ED3FF6235DEF6C0081F399 /* MultilineTextFieldSizer.swift in Sources */,
65ED3FF7235DEF6C0081F399 /* SearchFeedDelegate.swift in Sources */,
65ED3FF8235DEF6C0081F399 /* ErrorHandler.swift in Sources */,
@@ -4037,6 +4055,7 @@
6538131A2680E16C007A082C /* ExportOPMLWindowController.swift in Sources */,
65ED4021235DEF6C0081F399 /* NNW3Document.swift in Sources */,
65ED4022235DEF6C0081F399 /* ScriptingObject.swift in Sources */,
5117C1FE2B2DF3E900A30EAB /* SidebarWindowState.swift in Sources */,
65ED4023235DEF6C0081F399 /* Folder+Scriptability.swift in Sources */,
65ED4024235DEF6C0081F399 /* TimelineCellLayout.swift in Sources */,
51D205F028E3CF8D007C46EF /* LinkTextField.swift in Sources */,
@@ -4046,6 +4065,7 @@
65ED4027235DEF6C0081F399 /* UnreadIndicatorView.swift in Sources */,
DFEB034E2A273BFE00C7573A /* UTType.swift in Sources */,
51A9A5F22380DE520033AADF /* AddFeedDefaultContainer.swift in Sources */,
5117C1F82B2DF37800A30EAB /* DetailWindowState.swift in Sources */,
65ED4028235DEF6C0081F399 /* ExtractedArticle.swift in Sources */,
65ED4029235DEF6C0081F399 /* DeleteCommand.swift in Sources */,
65ED402A235DEF6C0081F399 /* AddFeedWindowController.swift in Sources */,
@@ -4326,6 +4346,7 @@
84E95D241FB1087500552D99 /* ArticlePasteboardWriter.swift in Sources */,
849A975B1ED9EB0D007D329B /* ArticleUtilities.swift in Sources */,
849ADEE8235981A0000E1B81 /* NNW3OpenPanelAccessoryViewController.swift in Sources */,
5117C1FD2B2DF3E900A30EAB /* SidebarWindowState.swift in Sources */,
849A975C1ED9EB0D007D329B /* DefaultFeedsImporter.swift in Sources */,
84A37CB5201ECD610087C5AF /* RenameWindowController.swift in Sources */,
84A14FF320048CA70046AD9A /* SendToMicroBlogCommand.swift in Sources */,
@@ -4336,6 +4357,7 @@
84B7178C201E66580091657D /* SidebarViewController+ContextualMenus.swift in Sources */,
842611A21FCB769D0086A189 /* RSHTMLMetadata+Extension.swift in Sources */,
84A1500520048DDF0046AD9A /* SendToMarsEditCommand.swift in Sources */,
5117C1FA2B2DF3B100A30EAB /* TimelineWindowState.swift in Sources */,
51FE10032345529D0056195D /* UserNotificationManager.swift in Sources */,
D5907DB22004BB37005947E5 /* ScriptingObjectContainer.swift in Sources */,
51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */,
@@ -4386,6 +4408,7 @@
8405DDA522168C62008CE1BF /* TimelineContainerViewController.swift in Sources */,
844B5B671FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift in Sources */,
848D578E21543519005FFAD5 /* PasteboardFeed.swift in Sources */,
5117C2002B2DF41B00A30EAB /* MainWindowState.swift in Sources */,
5144EA2F2279FAB600D19003 /* AccountsDetailViewController.swift in Sources */,
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,
173A64172547BE0900267F6E /* AccountType+Helpers.swift in Sources */,
@@ -4418,6 +4441,7 @@
DF3630EB2936183D00326FB8 /* OPMLDocument.swift in Sources */,
5144EA40227A37EC00D19003 /* ImportOPMLWindowController.swift in Sources */,
DFEB034D2A273BFE00C7573A /* UTType.swift in Sources */,
5117C1F72B2DF37800A30EAB /* DetailWindowState.swift in Sources */,
178A9F9D2549449F00AB7E9D /* AddAccountsView.swift in Sources */,
51C4CFF024D37D1F00AF9874 /* Secrets.swift in Sources */,
849A976D1ED9EBC8007D329B /* TimelineTableView.swift in Sources */,

View File

@@ -17,15 +17,7 @@ struct UserInfoKey {
static let itemIdentifier = "feedIdentifier"
static let windowState = "windowState"
static let windowFullScreenState = "windowFullScreenState"
static let containerExpandedWindowState = "containerExpandedWindowState"
static let readFeedsFilterState = "readFeedsFilterState"
static let readArticlesFilterState = "readArticlesFilterState"
static let readArticlesFilterStateKeys = "readArticlesFilterStateKey"
static let readArticlesFilterStateValues = "readArticlesFilterStateValue"
static let selectedFeedsState = "selectedFeedsState"
static let isShowingExtractedArticle = "isShowingExtractedArticle"
static let articleWindowScrollY = "articleWindowScrollY"
static let isSidebarHidden = "isSidebarHidden"
}