2017-09-23 03:37:25 +02:00
|
|
|
//
|
|
|
|
// AppDefaults.swift
|
2018-08-29 07:18:24 +02:00
|
|
|
// NetNewsWire
|
2017-09-23 03:37:25 +02:00
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 9/22/17.
|
|
|
|
// Copyright © 2017 Ranchero Software. All rights reserved.
|
|
|
|
//
|
|
|
|
|
2018-01-27 23:59:46 +01:00
|
|
|
import AppKit
|
2017-09-23 03:37:25 +02:00
|
|
|
|
2017-09-23 22:41:15 +02:00
|
|
|
enum FontSize: Int {
|
|
|
|
case small = 0
|
|
|
|
case medium = 1
|
|
|
|
case large = 2
|
|
|
|
case veryLarge = 3
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
struct AppDefaults {
|
2017-09-23 03:37:25 +02:00
|
|
|
|
2017-09-24 21:24:44 +02:00
|
|
|
struct Key {
|
2017-09-23 03:37:25 +02:00
|
|
|
static let firstRunDate = "firstRunDate"
|
2019-09-16 23:26:40 +02:00
|
|
|
static let lastImageCacheFlushDate = "lastImageCacheFlushDate"
|
2017-09-23 03:37:25 +02:00
|
|
|
static let sidebarFontSize = "sidebarFontSize"
|
|
|
|
static let timelineFontSize = "timelineFontSize"
|
2018-01-27 23:59:46 +01:00
|
|
|
static let timelineSortDirection = "timelineSortDirection"
|
2019-09-09 00:09:26 +02:00
|
|
|
static let timelineGroupByFeed = "timelineGroupByFeed"
|
2017-09-23 03:37:25 +02:00
|
|
|
static let detailFontSize = "detailFontSize"
|
|
|
|
static let openInBrowserInBackground = "openInBrowserInBackground"
|
2019-02-26 07:25:29 +01:00
|
|
|
static let mainWindowWidths = "mainWindowWidths"
|
2019-01-28 01:06:50 +01:00
|
|
|
static let refreshInterval = "refreshInterval"
|
2019-05-21 16:36:33 +02:00
|
|
|
static let addFeedAccountID = "addFeedAccountID"
|
|
|
|
static let addFolderAccountID = "addFolderAccountID"
|
2019-05-21 21:56:41 +02:00
|
|
|
static let importOPMLAccountID = "importOPMLAccountID"
|
2019-05-21 22:15:26 +02:00
|
|
|
static let exportOPMLAccountID = "exportOPMLAccountID"
|
2017-12-02 23:54:12 +01:00
|
|
|
|
|
|
|
// Hidden prefs
|
2019-09-02 18:13:21 +02:00
|
|
|
static let timelineShowsSeparators = "CorreiaSeparators"
|
2017-12-02 23:54:12 +01:00
|
|
|
static let showTitleOnMainWindow = "KafasisTitleMode"
|
2019-03-13 05:19:19 +01:00
|
|
|
static let hideDockUnreadCount = "JustinMillerHideDockUnreadCount"
|
2019-09-17 04:06:35 +02:00
|
|
|
|
2019-09-19 17:38:17 +02:00
|
|
|
#if !MAC_APP_STORE
|
|
|
|
static let webInspectorEnabled = "WebInspectorEnabled"
|
|
|
|
static let webInspectorStartsAttached = "__WebInspectorPageGroupLevel1__.WebKit2InspectorStartsAttached"
|
|
|
|
#endif
|
2017-09-23 03:37:25 +02:00
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
private static let smallestFontSizeRawValue = FontSize.small.rawValue
|
|
|
|
private static let largestFontSizeRawValue = FontSize.veryLarge.rawValue
|
2017-09-24 21:24:44 +02:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static let isFirstRun: Bool = {
|
|
|
|
if let _ = UserDefaults.standard.object(forKey: Key.firstRunDate) as? Date {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
firstRunDate = Date()
|
|
|
|
return true
|
|
|
|
}()
|
2017-09-23 21:17:14 +02:00
|
|
|
|
2019-09-16 23:26:40 +02:00
|
|
|
static var lastImageCacheFlushDate: Date? {
|
|
|
|
get {
|
|
|
|
return date(for: Key.lastImageCacheFlushDate)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setDate(for: Key.lastImageCacheFlushDate, newValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static var openInBrowserInBackground: Bool {
|
2017-09-23 03:37:25 +02:00
|
|
|
get {
|
2017-09-23 22:41:15 +02:00
|
|
|
return bool(for: Key.openInBrowserInBackground)
|
2017-09-23 03:37:25 +02:00
|
|
|
}
|
|
|
|
set {
|
2017-09-23 22:41:15 +02:00
|
|
|
setBool(for: Key.openInBrowserInBackground, newValue)
|
2017-09-23 03:37:25 +02:00
|
|
|
}
|
|
|
|
}
|
2017-09-23 22:41:15 +02:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static var sidebarFontSize: FontSize {
|
2017-09-23 03:37:25 +02:00
|
|
|
get {
|
2017-09-23 22:41:15 +02:00
|
|
|
return fontSize(for: Key.sidebarFontSize)
|
2017-09-23 03:37:25 +02:00
|
|
|
}
|
|
|
|
set {
|
2017-09-23 22:41:15 +02:00
|
|
|
setFontSize(for: Key.sidebarFontSize, newValue)
|
|
|
|
}
|
|
|
|
}
|
2017-11-25 06:39:59 +01:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static var timelineFontSize: FontSize {
|
2017-09-23 22:41:15 +02:00
|
|
|
get {
|
|
|
|
return fontSize(for: Key.timelineFontSize)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setFontSize(for: Key.timelineFontSize, newValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static var detailFontSize: FontSize {
|
2017-09-23 22:41:15 +02:00
|
|
|
get {
|
|
|
|
return fontSize(for: Key.detailFontSize)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setFontSize(for: Key.detailFontSize, newValue)
|
2017-09-23 03:37:25 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 16:36:33 +02:00
|
|
|
static var addFeedAccountID: String? {
|
|
|
|
get {
|
|
|
|
return string(for: Key.addFeedAccountID)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setString(for: Key.addFeedAccountID, newValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static var addFolderAccountID: String? {
|
|
|
|
get {
|
|
|
|
return string(for: Key.addFolderAccountID)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setString(for: Key.addFolderAccountID, newValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 21:56:41 +02:00
|
|
|
static var importOPMLAccountID: String? {
|
|
|
|
get {
|
|
|
|
return string(for: Key.importOPMLAccountID)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setString(for: Key.importOPMLAccountID, newValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-21 22:15:26 +02:00
|
|
|
static var exportOPMLAccountID: String? {
|
|
|
|
get {
|
|
|
|
return string(for: Key.exportOPMLAccountID)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setString(for: Key.exportOPMLAccountID, newValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static var showTitleOnMainWindow: Bool {
|
2017-12-02 23:54:12 +01:00
|
|
|
return bool(for: Key.showTitleOnMainWindow)
|
|
|
|
}
|
|
|
|
|
2019-03-08 20:35:37 +01:00
|
|
|
static var hideDockUnreadCount: Bool {
|
|
|
|
return bool(for: Key.hideDockUnreadCount)
|
|
|
|
}
|
|
|
|
|
2019-09-19 17:38:17 +02:00
|
|
|
#if !MAC_APP_STORE
|
|
|
|
static var webInspectorEnabled: Bool {
|
|
|
|
get {
|
|
|
|
return bool(for: Key.webInspectorEnabled)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setBool(for: Key.webInspectorEnabled, newValue)
|
|
|
|
}
|
2019-09-17 04:06:35 +02:00
|
|
|
}
|
|
|
|
|
2019-09-19 17:38:17 +02:00
|
|
|
static var webInspectorStartsAttached: Bool {
|
|
|
|
get {
|
|
|
|
return bool(for: Key.webInspectorStartsAttached)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setBool(for: Key.webInspectorStartsAttached, newValue)
|
|
|
|
}
|
2019-09-17 20:42:18 +02:00
|
|
|
}
|
2019-09-19 17:38:17 +02:00
|
|
|
#endif
|
2019-09-17 20:42:18 +02:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static var timelineSortDirection: ComparisonResult {
|
2018-01-27 23:59:46 +01:00
|
|
|
get {
|
|
|
|
return sortDirection(for: Key.timelineSortDirection)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setSortDirection(for: Key.timelineSortDirection, newValue)
|
|
|
|
}
|
|
|
|
}
|
2019-09-02 18:13:21 +02:00
|
|
|
|
2019-09-09 00:09:26 +02:00
|
|
|
static var timelineGroupByFeed: Bool {
|
|
|
|
get {
|
|
|
|
return bool(for: Key.timelineGroupByFeed)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
setBool(for: Key.timelineGroupByFeed, newValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-02 18:13:21 +02:00
|
|
|
static var timelineShowsSeparators: Bool {
|
|
|
|
return bool(for: Key.timelineShowsSeparators)
|
|
|
|
}
|
2018-01-27 23:59:46 +01:00
|
|
|
|
2019-02-26 07:25:29 +01:00
|
|
|
static var mainWindowWidths: [Int]? {
|
|
|
|
get {
|
|
|
|
return UserDefaults.standard.object(forKey: Key.mainWindowWidths) as? [Int]
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
UserDefaults.standard.set(newValue, forKey: Key.mainWindowWidths)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-28 01:06:50 +01:00
|
|
|
static var refreshInterval: RefreshInterval {
|
|
|
|
get {
|
|
|
|
let rawValue = UserDefaults.standard.integer(forKey: Key.refreshInterval)
|
|
|
|
return RefreshInterval(rawValue: rawValue) ?? RefreshInterval.everyHour
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
UserDefaults.standard.set(newValue.rawValue, forKey: Key.refreshInterval)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func registerDefaults() {
|
2019-09-16 23:37:18 +02:00
|
|
|
let defaults: [String : Any] = [Key.lastImageCacheFlushDate: Date(),
|
|
|
|
Key.sidebarFontSize: FontSize.medium.rawValue,
|
2019-09-09 00:09:26 +02:00
|
|
|
Key.timelineFontSize: FontSize.medium.rawValue,
|
|
|
|
Key.detailFontSize: FontSize.medium.rawValue,
|
|
|
|
Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue,
|
|
|
|
Key.timelineGroupByFeed: false,
|
|
|
|
"NSScrollViewShouldScrollUnderTitlebar": false,
|
|
|
|
Key.refreshInterval: RefreshInterval.everyHour.rawValue]
|
2017-09-24 21:24:44 +02:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
UserDefaults.standard.register(defaults: defaults)
|
2019-02-15 23:40:05 +01:00
|
|
|
|
|
|
|
// It seems that registering a default for NSQuitAlwaysKeepsWindows to true
|
|
|
|
// is not good enough to get the system to respect it, so we have to literally
|
|
|
|
// set it as the default to get it to take effect. This overrides a system-wide
|
|
|
|
// setting in the System Preferences, which is ostensibly meant to "close windows"
|
|
|
|
// in an app, but has the side-effect of also not preserving or restoring any state
|
|
|
|
// for the window. Since we've switched to using the standard state preservation and
|
|
|
|
// restoration mechanisms, and because it seems highly unlikely any user would object
|
|
|
|
// to NetNewsWire preserving this state, we'll force the preference on. If this becomes
|
|
|
|
// an issue, this could be changed to proactively look for whether the default has been
|
|
|
|
// set _by the user_ to false, and respect that default if it is so-set.
|
2019-02-26 07:25:29 +01:00
|
|
|
// UserDefaults.standard.set(true, forKey: "NSQuitAlwaysKeepsWindows")
|
|
|
|
|
|
|
|
// TODO: revisit the above when coming back to state restoration issues.
|
2017-09-23 03:37:25 +02:00
|
|
|
}
|
2017-11-25 06:39:59 +01:00
|
|
|
|
|
|
|
static func actualFontSize(for fontSize: FontSize) -> CGFloat {
|
|
|
|
switch fontSize {
|
|
|
|
case .small:
|
|
|
|
return NSFont.systemFontSize
|
|
|
|
case .medium:
|
|
|
|
return actualFontSize(for: .small) + 1.0
|
|
|
|
case .large:
|
|
|
|
return actualFontSize(for: .medium) + 4.0
|
|
|
|
case .veryLarge:
|
|
|
|
return actualFontSize(for: .large) + 8.0
|
|
|
|
}
|
|
|
|
}
|
2017-09-23 22:41:15 +02:00
|
|
|
}
|
2017-09-23 03:37:25 +02:00
|
|
|
|
2017-09-23 22:41:15 +02:00
|
|
|
private extension AppDefaults {
|
2017-09-23 03:37:25 +02:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static var firstRunDate: Date? {
|
2017-09-23 22:41:15 +02:00
|
|
|
get {
|
|
|
|
return date(for: Key.firstRunDate)
|
|
|
|
}
|
|
|
|
set {
|
2017-09-24 21:24:44 +02:00
|
|
|
setDate(for: Key.firstRunDate, newValue)
|
2017-09-23 22:41:15 +02:00
|
|
|
}
|
|
|
|
}
|
2017-09-23 03:37:25 +02:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func fontSize(for key: String) -> FontSize {
|
2018-02-12 03:58:01 +01:00
|
|
|
// Punted till after 1.0.
|
|
|
|
return .medium
|
|
|
|
|
|
|
|
// var rawFontSize = int(for: key)
|
|
|
|
// if rawFontSize < smallestFontSizeRawValue {
|
|
|
|
// rawFontSize = smallestFontSizeRawValue
|
|
|
|
// }
|
|
|
|
// if rawFontSize > largestFontSizeRawValue {
|
|
|
|
// rawFontSize = largestFontSizeRawValue
|
|
|
|
// }
|
|
|
|
// return FontSize(rawValue: rawFontSize)!
|
2017-09-23 22:41:15 +02:00
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func setFontSize(for key: String, _ fontSize: FontSize) {
|
2017-09-23 22:41:15 +02:00
|
|
|
setInt(for: key, fontSize.rawValue)
|
|
|
|
}
|
|
|
|
|
2019-05-21 16:36:33 +02:00
|
|
|
static func string(for key: String) -> String? {
|
|
|
|
return UserDefaults.standard.string(forKey: key)
|
|
|
|
}
|
|
|
|
|
|
|
|
static func setString(for key: String, _ value: String?) {
|
|
|
|
UserDefaults.standard.set(value, forKey: key)
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func bool(for key: String) -> Bool {
|
2017-09-23 03:37:25 +02:00
|
|
|
return UserDefaults.standard.bool(forKey: key)
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func setBool(for key: String, _ flag: Bool) {
|
2017-09-23 03:37:25 +02:00
|
|
|
UserDefaults.standard.set(flag, forKey: key)
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func int(for key: String) -> Int {
|
2017-09-23 22:41:15 +02:00
|
|
|
return UserDefaults.standard.integer(forKey: key)
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func setInt(for key: String, _ x: Int) {
|
2017-09-23 22:41:15 +02:00
|
|
|
UserDefaults.standard.set(x, forKey: key)
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func date(for key: String) -> Date? {
|
2017-09-23 03:37:25 +02:00
|
|
|
return UserDefaults.standard.object(forKey: key) as? Date
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func setDate(for key: String, _ date: Date?) {
|
2017-09-23 03:37:25 +02:00
|
|
|
UserDefaults.standard.set(date, forKey: key)
|
|
|
|
}
|
2018-01-27 23:59:46 +01:00
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func sortDirection(for key:String) -> ComparisonResult {
|
2018-01-27 23:59:46 +01:00
|
|
|
let rawInt = int(for: key)
|
|
|
|
if rawInt == ComparisonResult.orderedAscending.rawValue {
|
|
|
|
return .orderedAscending
|
|
|
|
}
|
|
|
|
return .orderedDescending
|
|
|
|
}
|
|
|
|
|
2018-12-28 06:34:21 +01:00
|
|
|
static func setSortDirection(for key: String, _ value: ComparisonResult) {
|
2018-01-27 23:59:46 +01:00
|
|
|
if value == .orderedAscending {
|
|
|
|
setInt(for: key, ComparisonResult.orderedAscending.rawValue)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setInt(for: key, ComparisonResult.orderedDescending.rawValue)
|
|
|
|
}
|
|
|
|
}
|
2017-09-23 03:37:25 +02:00
|
|
|
}
|
|
|
|
|
2019-09-02 18:13:21 +02:00
|
|
|
// MARK: -
|
|
|
|
|
|
|
|
extension UserDefaults {
|
|
|
|
/// This property exists so that it can conveniently be observed via KVO
|
|
|
|
@objc var CorreiaSeparators: Bool {
|
|
|
|
get {
|
|
|
|
return bool(forKey: AppDefaults.Key.timelineShowsSeparators)
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
set(newValue, forKey: AppDefaults.Key.timelineShowsSeparators)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-09-23 03:37:25 +02:00
|
|
|
|