Merge pull request #2161 from stuartbreckenridge/swiftui

AppSettings
This commit is contained in:
Maurice Parker
2020-07-02 04:53:53 -05:00
committed by GitHub
51 changed files with 718 additions and 603 deletions

View File

@@ -15,6 +15,9 @@ enum FontSize: Int {
}
struct AppDefaults {
static var shared = AppDefaults()
private init() {}
struct Key {
static let firstRunDate = "firstRunDate"
@@ -50,14 +53,14 @@ struct AppDefaults {
private static let smallestFontSizeRawValue = FontSize.small.rawValue
private static let largestFontSizeRawValue = FontSize.veryLarge.rawValue
static let isDeveloperBuild: Bool = {
let isDeveloperBuild: Bool = {
if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" {
return true
}
return false
}()
static let isFirstRun: Bool = {
var isFirstRun: Bool = {
if let _ = UserDefaults.standard.object(forKey: Key.firstRunDate) as? Date {
return false
}
@@ -65,7 +68,7 @@ struct AppDefaults {
return true
}()
static var windowState: [AnyHashable : Any]? {
var windowState: [AnyHashable : Any]? {
get {
return UserDefaults.standard.object(forKey: Key.windowState) as? [AnyHashable : Any]
}
@@ -74,7 +77,7 @@ struct AppDefaults {
}
}
static var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
get {
return UserDefaults.standard.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
}
@@ -83,160 +86,160 @@ struct AppDefaults {
}
}
static var lastImageCacheFlushDate: Date? {
var lastImageCacheFlushDate: Date? {
get {
return date(for: Key.lastImageCacheFlushDate)
return AppDefaults.date(for: Key.lastImageCacheFlushDate)
}
set {
setDate(for: Key.lastImageCacheFlushDate, newValue)
AppDefaults.setDate(for: Key.lastImageCacheFlushDate, newValue)
}
}
static var openInBrowserInBackground: Bool {
var openInBrowserInBackground: Bool {
get {
return bool(for: Key.openInBrowserInBackground)
return AppDefaults.bool(for: Key.openInBrowserInBackground)
}
set {
setBool(for: Key.openInBrowserInBackground, newValue)
AppDefaults.setBool(for: Key.openInBrowserInBackground, newValue)
}
}
static var sidebarFontSize: FontSize {
var sidebarFontSize: FontSize {
get {
return fontSize(for: Key.sidebarFontSize)
}
set {
setFontSize(for: Key.sidebarFontSize, newValue)
AppDefaults.setFontSize(for: Key.sidebarFontSize, newValue)
}
}
static var timelineFontSize: FontSize {
var timelineFontSize: FontSize {
get {
return fontSize(for: Key.timelineFontSize)
}
set {
setFontSize(for: Key.timelineFontSize, newValue)
AppDefaults.setFontSize(for: Key.timelineFontSize, newValue)
}
}
static var detailFontSize: FontSize {
var detailFontSize: FontSize {
get {
return fontSize(for: Key.detailFontSize)
}
set {
setFontSize(for: Key.detailFontSize, newValue)
AppDefaults.setFontSize(for: Key.detailFontSize, newValue)
}
}
static var addWebFeedAccountID: String? {
var addWebFeedAccountID: String? {
get {
return string(for: Key.addWebFeedAccountID)
return AppDefaults.string(for: Key.addWebFeedAccountID)
}
set {
setString(for: Key.addWebFeedAccountID, newValue)
AppDefaults.setString(for: Key.addWebFeedAccountID, newValue)
}
}
static var addWebFeedFolderName: String? {
var addWebFeedFolderName: String? {
get {
return string(for: Key.addWebFeedFolderName)
return AppDefaults.string(for: Key.addWebFeedFolderName)
}
set {
setString(for: Key.addWebFeedFolderName, newValue)
AppDefaults.setString(for: Key.addWebFeedFolderName, newValue)
}
}
static var addFolderAccountID: String? {
var addFolderAccountID: String? {
get {
return string(for: Key.addFolderAccountID)
return AppDefaults.string(for: Key.addFolderAccountID)
}
set {
setString(for: Key.addFolderAccountID, newValue)
AppDefaults.setString(for: Key.addFolderAccountID, newValue)
}
}
static var importOPMLAccountID: String? {
var importOPMLAccountID: String? {
get {
return string(for: Key.importOPMLAccountID)
return AppDefaults.string(for: Key.importOPMLAccountID)
}
set {
setString(for: Key.importOPMLAccountID, newValue)
AppDefaults.setString(for: Key.importOPMLAccountID, newValue)
}
}
static var exportOPMLAccountID: String? {
var exportOPMLAccountID: String? {
get {
return string(for: Key.exportOPMLAccountID)
return AppDefaults.string(for: Key.exportOPMLAccountID)
}
set {
setString(for: Key.exportOPMLAccountID, newValue)
AppDefaults.setString(for: Key.exportOPMLAccountID, newValue)
}
}
static var defaultBrowserID: String? {
var defaultBrowserID: String? {
get {
return string(for: Key.defaultBrowserID)
return AppDefaults.string(for: Key.defaultBrowserID)
}
set {
setString(for: Key.defaultBrowserID, newValue)
AppDefaults.setString(for: Key.defaultBrowserID, newValue)
}
}
static var showTitleOnMainWindow: Bool {
return bool(for: Key.showTitleOnMainWindow)
var showTitleOnMainWindow: Bool {
return AppDefaults.bool(for: Key.showTitleOnMainWindow)
}
static var showDebugMenu: Bool {
return bool(for: Key.showDebugMenu)
var showDebugMenu: Bool {
return AppDefaults.bool(for: Key.showDebugMenu)
}
static var hideDockUnreadCount: Bool {
return bool(for: Key.hideDockUnreadCount)
var hideDockUnreadCount: Bool {
return AppDefaults.bool(for: Key.hideDockUnreadCount)
}
#if !MAC_APP_STORE
static var webInspectorEnabled: Bool {
var webInspectorEnabled: Bool {
get {
return bool(for: Key.webInspectorEnabled)
return AppDefaults.bool(for: Key.webInspectorEnabled)
}
set {
setBool(for: Key.webInspectorEnabled, newValue)
AppDefaults.setBool(for: Key.webInspectorEnabled, newValue)
}
}
static var webInspectorStartsAttached: Bool {
var webInspectorStartsAttached: Bool {
get {
return bool(for: Key.webInspectorStartsAttached)
return AppDefaults.bool(for: Key.webInspectorStartsAttached)
}
set {
setBool(for: Key.webInspectorStartsAttached, newValue)
AppDefaults.setBool(for: Key.webInspectorStartsAttached, newValue)
}
}
#endif
static var timelineSortDirection: ComparisonResult {
var timelineSortDirection: ComparisonResult {
get {
return sortDirection(for: Key.timelineSortDirection)
return AppDefaults.sortDirection(for: Key.timelineSortDirection)
}
set {
setSortDirection(for: Key.timelineSortDirection, newValue)
AppDefaults.setSortDirection(for: Key.timelineSortDirection, newValue)
}
}
static var timelineGroupByFeed: Bool {
var timelineGroupByFeed: Bool {
get {
return bool(for: Key.timelineGroupByFeed)
return AppDefaults.bool(for: Key.timelineGroupByFeed)
}
set {
setBool(for: Key.timelineGroupByFeed, newValue)
AppDefaults.setBool(for: Key.timelineGroupByFeed, newValue)
}
}
static var timelineShowsSeparators: Bool {
return bool(for: Key.timelineShowsSeparators)
var timelineShowsSeparators: Bool {
return AppDefaults.bool(for: Key.timelineShowsSeparators)
}
static var refreshInterval: RefreshInterval {
var refreshInterval: RefreshInterval {
get {
let rawValue = UserDefaults.standard.integer(forKey: Key.refreshInterval)
return RefreshInterval(rawValue: rawValue) ?? RefreshInterval.everyHour
@@ -246,7 +249,7 @@ struct AppDefaults {
}
}
static func registerDefaults() {
func registerDefaults() {
#if DEBUG
let showDebugMenu = true
#else
@@ -278,7 +281,7 @@ struct AppDefaults {
// TODO: revisit the above when coming back to state restoration issues.
}
static func actualFontSize(for fontSize: FontSize) -> CGFloat {
func actualFontSize(for fontSize: FontSize) -> CGFloat {
switch fontSize {
case .small:
return NSFont.systemFontSize
@@ -296,14 +299,14 @@ private extension AppDefaults {
static var firstRunDate: Date? {
get {
return date(for: Key.firstRunDate)
return AppDefaults.date(for: Key.firstRunDate)
}
set {
setDate(for: Key.firstRunDate, newValue)
AppDefaults.setDate(for: Key.firstRunDate, newValue)
}
}
static func fontSize(for key: String) -> FontSize {
func fontSize(for key: String) -> FontSize {
// Punted till after 1.0.
return .medium

View File

@@ -191,8 +191,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
}
#endif
AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.isFirstRun
AppDefaults.shared.registerDefaults()
let isFirstRun = AppDefaults.shared.isFirstRun
if isFirstRun {
logDebugMessage("Is first run.")
}
@@ -237,7 +237,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
UNUserNotificationCenter.current().delegate = self
userNotificationManager = UserNotificationManager()
if AppDefaults.showDebugMenu {
if AppDefaults.shared.showDebugMenu {
refreshTimer!.update()
syncTimer!.update()
@@ -417,7 +417,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
}
#if !MAC_APP_STORE
if item.action == #selector(toggleWebInspectorEnabled(_:)) {
(item as! NSMenuItem).state = AppDefaults.webInspectorEnabled ? .on : .off
(item as! NSMenuItem).state = AppDefaults.shared.webInspectorEnabled ? .on : .off
}
#endif
return true
@@ -447,7 +447,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
// MARK: - Dock Badge
@objc func updateDockBadge() {
let label = unreadCount > 0 && !AppDefaults.hideDockUnreadCount ? "\(unreadCount)" : ""
let label = unreadCount > 0 && !AppDefaults.shared.hideDockUnreadCount ? "\(unreadCount)" : ""
NSApplication.shared.dockTile.badgeLabel = label
}
@@ -624,16 +624,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
@IBAction func sortByOldestArticleOnTop(_ sender: Any?) {
AppDefaults.timelineSortDirection = .orderedAscending
AppDefaults.shared.timelineSortDirection = .orderedAscending
}
@IBAction func sortByNewestArticleOnTop(_ sender: Any?) {
AppDefaults.timelineSortDirection = .orderedDescending
AppDefaults.shared.timelineSortDirection = .orderedDescending
}
@IBAction func groupByFeedToggled(_ sender: NSMenuItem) {
AppDefaults.timelineGroupByFeed.toggle()
AppDefaults.shared.timelineGroupByFeed.toggle()
}
@IBAction func checkForUpdates(_ sender: Any?) {
@@ -680,13 +680,13 @@ extension AppDelegate {
@IBAction func toggleWebInspectorEnabled(_ sender: Any?) {
#if !MAC_APP_STORE
let newValue = !AppDefaults.webInspectorEnabled
AppDefaults.webInspectorEnabled = newValue
let newValue = !AppDefaults.shared.webInspectorEnabled
AppDefaults.shared.webInspectorEnabled = newValue
// An attached inspector can display incorrectly on certain setups (like mine); default to displaying in a separate window,
// and reset the default to a separate window when the preference is toggled off and on again in case the inspector is
// accidentally reattached.
AppDefaults.webInspectorStartsAttached = false
AppDefaults.shared.webInspectorStartsAttached = false
NotificationCenter.default.post(name: .WebInspectorEnabledDidChange, object: newValue)
#endif
}
@@ -717,13 +717,13 @@ private extension AppDelegate {
func updateSortMenuItems() {
let sortByNewestOnTop = AppDefaults.timelineSortDirection == .orderedDescending
let sortByNewestOnTop = AppDefaults.shared.timelineSortDirection == .orderedDescending
sortByNewestArticleOnTopMenuItem.state = sortByNewestOnTop ? .on : .off
sortByOldestArticleOnTopMenuItem.state = sortByNewestOnTop ? .off : .on
}
func updateGroupByFeedMenuItem() {
let groupByFeedEnabled = AppDefaults.timelineGroupByFeed
let groupByFeedEnabled = AppDefaults.shared.timelineGroupByFeed
groupArticlesByFeedMenuItem.state = groupByFeedEnabled ? .on : .off
}
}

View File

@@ -16,7 +16,7 @@ struct Browser {
/// The user-assigned default browser, or `nil` if none was assigned
/// (i.e., the system default should be used).
static var defaultBrowser: MacWebBrowser? {
if let bundleID = AppDefaults.defaultBrowserID, let browser = MacWebBrowser(bundleIdentifier: bundleID) {
if let bundleID = AppDefaults.shared.defaultBrowserID, let browser = MacWebBrowser(bundleIdentifier: bundleID) {
return browser
}
@@ -31,7 +31,7 @@ struct Browser {
/// - invert: Whether to invert the "open in background in browser" preference
static func open(_ urlString: String, invertPreference invert: Bool = false) {
// Opens according to prefs.
open(urlString, inBackground: invert ? !AppDefaults.openInBrowserInBackground : AppDefaults.openInBrowserInBackground)
open(urlString, inBackground: invert ? !AppDefaults.shared.openInBrowserInBackground : AppDefaults.shared.openInBrowserInBackground)
}
@@ -56,7 +56,7 @@ struct Browser {
extension Browser {
static var titleForOpenInBrowserInverted: String {
let openInBackgroundPref = AppDefaults.openInBrowserInBackground
let openInBackgroundPref = AppDefaults.shared.openInBrowserInBackground
return openInBackgroundPref ?
NSLocalizedString("Open in Browser in Foreground", comment: "Open in Browser in Foreground menu item title") :

View File

@@ -36,7 +36,7 @@ class AddFolderWindowController : NSWindowController {
// MARK: - NSViewController
override func windowDidLoad() {
let preferredAccountID = AppDefaults.addFolderAccountID
let preferredAccountID = AppDefaults.shared.addFolderAccountID
accountPopupButton.removeAllItems()
let menu = NSMenu()
@@ -93,7 +93,7 @@ private extension AddFolderWindowController {
}
let account = menuItem.representedObject as! Account
AppDefaults.addFolderAccountID = account.accountID
AppDefaults.shared.addFolderAccountID = account.accountID
let folderName = self.folderNameTextField.stringValue
if folderName.isEmpty {

View File

@@ -116,7 +116,7 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
waitingForFirstReload = true
#if !MAC_APP_STORE
webInspectorEnabled = AppDefaults.webInspectorEnabled
webInspectorEnabled = AppDefaults.shared.webInspectorEnabled
NotificationCenter.default.addObserver(self, selector: #selector(webInspectorEnabledDidChange(_:)), name: .WebInspectorEnabledDidChange, object: nil)
#endif

View File

@@ -61,7 +61,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
sharingServicePickerDelegate = SharingServicePickerDelegate(self.window)
if !AppDefaults.showTitleOnMainWindow {
if !AppDefaults.shared.showTitleOnMainWindow {
window?.titleVisibility = .hidden
}
@@ -116,12 +116,12 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
}
func saveStateToUserDefaults() {
AppDefaults.windowState = savableState()
AppDefaults.shared.windowState = savableState()
window?.saveFrame(usingName: windowAutosaveName)
}
func restoreStateFromUserDefaults() {
if let state = AppDefaults.windowState {
if let state = AppDefaults.shared.windowState {
restoreState(from: state)
window?.setFrameUsingName(windowAutosaveName, force: true)
}
@@ -281,7 +281,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
@IBAction func openInBrowserUsingOppositeOfSettings(_ sender: Any?) {
if let link = currentLink {
Browser.open(link, inBackground: !AppDefaults.openInBrowserInBackground)
Browser.open(link, inBackground: !AppDefaults.shared.openInBrowserInBackground)
}
}
@@ -819,7 +819,7 @@ private extension MainWindowController {
}
func validateToggleArticleExtractor(_ item: NSValidatedUserInterfaceItem) -> Bool {
guard !AppDefaults.isDeveloperBuild else {
guard !AppDefaults.shared.isDeveloperBuild else {
return false
}

View File

@@ -82,7 +82,7 @@ private extension NNW3ImportController {
guard let account = accessoryViewController.selectedAccount else {
return
}
AppDefaults.importOPMLAccountID = account.accountID
AppDefaults.shared.importOPMLAccountID = account.accountID
NNW3ImportController.importSubscriptionsPlist(subscriptionsPlistURL, into: account)
}

View File

@@ -39,7 +39,7 @@ final class NNW3OpenPanelAccessoryViewController: NSViewController {
menuItem.representedObject = account
menu.addItem(menuItem)
if account.accountID == AppDefaults.importOPMLAccountID {
if account.accountID == AppDefaults.shared.importOPMLAccountID {
accountPopUpButton.select(menuItem)
}
}

View File

@@ -31,7 +31,7 @@ class ExportOPMLWindowController: NSWindowController {
oneMenuItem.representedObject = oneAccount
menu.addItem(oneMenuItem)
if oneAccount.accountID == AppDefaults.exportOPMLAccountID {
if oneAccount.accountID == AppDefaults.shared.exportOPMLAccountID {
accountPopUpButton.select(oneMenuItem)
}
@@ -66,7 +66,7 @@ class ExportOPMLWindowController: NSWindowController {
}
let account = menuItem.representedObject as! Account
AppDefaults.exportOPMLAccountID = account.accountID
AppDefaults.shared.exportOPMLAccountID = account.accountID
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK)
exportOPML(account: account)

View File

@@ -35,7 +35,7 @@ class ImportOPMLWindowController: NSWindowController {
oneMenuItem.representedObject = oneAccount
menu.addItem(oneMenuItem)
if oneAccount.accountID == AppDefaults.importOPMLAccountID {
if oneAccount.accountID == AppDefaults.shared.importOPMLAccountID {
accountPopUpButton.select(oneMenuItem)
}
@@ -70,7 +70,7 @@ class ImportOPMLWindowController: NSWindowController {
}
let account = menuItem.representedObject as! Account
AppDefaults.importOPMLAccountID = account.accountID
AppDefaults.shared.importOPMLAccountID = account.accountID
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK)
importOPML(account: account)

View File

@@ -17,7 +17,7 @@ struct SidebarCellAppearance: Equatable {
let textFieldFont: NSFont
init(fontSize: FontSize) {
self.textFieldFontSize = AppDefaults.actualFontSize(for: fontSize)
self.textFieldFontSize = AppDefaults.shared.actualFontSize(for: fontSize)
self.textFieldFont = NSFont.systemFont(ofSize: textFieldFontSize)
}
}

View File

@@ -57,7 +57,7 @@ protocol SidebarDelegate: class {
// MARK: - NSViewController
override func viewDidLoad() {
sidebarCellAppearance = SidebarCellAppearance(fontSize: AppDefaults.sidebarFontSize)
sidebarCellAppearance = SidebarCellAppearance(fontSize: AppDefaults.shared.sidebarFontSize)
outlineView.dataSource = dataSource
outlineView.doubleAction = #selector(doubleClickedSidebar(_:))

View File

@@ -44,7 +44,7 @@ struct TimelineCellAppearance: Equatable {
init(showIcon: Bool, fontSize: FontSize) {
let actualFontSize = AppDefaults.actualFontSize(for: fontSize)
let actualFontSize = AppDefaults.shared.actualFontSize(for: fontSize)
let smallItemFontSize = floor(actualFontSize * 0.90)
let largeItemFontSize = actualFontSize

View File

@@ -27,7 +27,7 @@ class TimelineTableCellView: NSTableCellView {
return [self.dateView, self.feedNameView, self.titleView, self.summaryView, self.textView]
}()
private var showsSeparator: Bool = AppDefaults.timelineShowsSeparators {
private var showsSeparator: Bool = AppDefaults.shared.timelineShowsSeparators {
didSet {
separatorView.isHidden = !showsSeparator
}
@@ -85,7 +85,7 @@ class TimelineTableCellView: NSTableCellView {
}
func timelineShowsSeparatorsDefaultDidChange() {
showsSeparator = AppDefaults.timelineShowsSeparators
showsSeparator = AppDefaults.shared.timelineShowsSeparators
}
override func setFrameSize(_ newSize: NSSize) {
@@ -209,7 +209,7 @@ private extension TimelineTableCellView {
addSubviewAtInit(feedNameView, hidden: true)
addSubviewAtInit(iconView, hidden: true)
addSubviewAtInit(starView, hidden: true)
addSubviewAtInit(separatorView, hidden: !AppDefaults.timelineShowsSeparators)
addSubviewAtInit(separatorView, hidden: !AppDefaults.shared.timelineShowsSeparators)
makeTextFieldColorsNormal()
}

View File

@@ -167,7 +167,7 @@ private extension TimelineContainerViewController {
func updateViewOptionsPopUpButton() {
let localizedTitle = NSLocalizedString("Sort %@", comment: "Sort")
if AppDefaults.timelineSortDirection == .orderedAscending {
if AppDefaults.shared.timelineSortDirection == .orderedAscending {
newestToOldestMenuItem.state = .off
oldestToNewestMenuItem.state = .on
let title = NSString.localizedStringWithFormat(localizedTitle as NSString, oldestToNewestMenuItem.title) as String
@@ -179,7 +179,7 @@ private extension TimelineContainerViewController {
viewOptionsPopUpButton.setTitle(title)
}
if AppDefaults.timelineGroupByFeed == true {
if AppDefaults.shared.timelineGroupByFeed == true {
groupByFeedMenuItem.state = .on
} else {
groupByFeedMenuItem.state = .off

View File

@@ -142,21 +142,21 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
private var didRegisterForNotifications = false
static let fetchAndMergeArticlesQueue = CoalescingQueue(name: "Fetch and Merge Articles", interval: 0.5, maxInterval: 2.0)
private var sortDirection = AppDefaults.timelineSortDirection {
private var sortDirection = AppDefaults.shared.timelineSortDirection {
didSet {
if sortDirection != oldValue {
sortParametersDidChange()
}
}
}
private var groupByFeed = AppDefaults.timelineGroupByFeed {
private var groupByFeed = AppDefaults.shared.timelineGroupByFeed {
didSet {
if groupByFeed != oldValue {
sortParametersDidChange()
}
}
}
private var fontSize: FontSize = AppDefaults.timelineFontSize {
private var fontSize: FontSize = AppDefaults.shared.timelineFontSize {
didSet {
if fontSize != oldValue {
fontSizeDidChange()
@@ -611,9 +611,9 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
}
@objc func userDefaultsDidChange(_ note: Notification) {
self.fontSize = AppDefaults.timelineFontSize
self.sortDirection = AppDefaults.timelineSortDirection
self.groupByFeed = AppDefaults.timelineGroupByFeed
self.fontSize = AppDefaults.shared.timelineFontSize
self.sortDirection = AppDefaults.shared.timelineSortDirection
self.groupByFeed = AppDefaults.shared.timelineGroupByFeed
}
// MARK: - Reloading Data

View File

@@ -171,7 +171,7 @@ private extension AccountsAddViewController {
}
}
if AppDefaults.isDeveloperBuild {
if AppDefaults.shared.isDeveloperBuild {
removeAccountType(.cloudKit)
removeAccountType(.feedly)
removeAccountType(.feedWrangler)

View File

@@ -55,7 +55,7 @@ final class GeneralPreferencesViewController: NSViewController {
return
}
let bundleID = menuItem.representedObject as? String
AppDefaults.defaultBrowserID = bundleID
AppDefaults.shared.defaultBrowserID = bundleID
updateUI()
}
}
@@ -164,7 +164,7 @@ private extension GeneralPreferencesViewController {
menu.addItem(item)
}
defaultBrowserPopup.selectItem(at: defaultBrowserPopup.indexOfItem(withRepresentedObject: AppDefaults.defaultBrowserID))
defaultBrowserPopup.selectItem(at: defaultBrowserPopup.indexOfItem(withRepresentedObject: AppDefaults.shared.defaultBrowserID))
}
}

View File

@@ -2,11 +2,12 @@
// AppDefaults.swift
// NetNewsWire
//
// Created by Maurice Parker on 6/28/20.
// Created by Stuart Breckenridge on 1/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
import SwiftUI
enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
case automatic = 0
@@ -23,285 +24,330 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
return NSLocalizedString("Dark", comment: "Dark")
}
}
}
struct AppDefaults {
enum FontSize: Int {
case small = 0
case medium = 1
case large = 2
case veryLarge = 3
}
final class AppDefaults: ObservableObject {
#if os(macOS)
static var shared: UserDefaults = UserDefaults.standard
static let store: UserDefaults = UserDefaults.standard
#endif
#if os(iOS)
static var shared: UserDefaults = {
static let store: UserDefaults = {
let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String
let suiteName = "\(appIdentifierPrefix)group.\(Bundle.main.bundleIdentifier!)"
return UserDefaults.init(suiteName: suiteName)!
}()
#endif
public static let shared = AppDefaults()
private init() {}
struct Key {
// Shared Defaults
static let refreshInterval = "refreshInterval"
static let hideDockUnreadCount = "JustinMillerHideDockUnreadCount"
static let userInterfaceColorPalette = "userInterfaceColorPalette"
static let activeExtensionPointIDs = "activeExtensionPointIDs"
static let lastImageCacheFlushDate = "lastImageCacheFlushDate"
static let firstRunDate = "firstRunDate"
static let timelineGroupByFeed = "timelineGroupByFeed"
static let refreshClearsReadArticles = "refreshClearsReadArticles"
static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let timelineSortDirection = "timelineSortDirection"
static let articleFullscreenAvailable = "articleFullscreenAvailable"
static let articleFullscreenEnabled = "articleFullscreenEnabled"
static let confirmMarkAllAsRead = "confirmMarkAllAsRead"
static let lastRefresh = "lastRefresh"
static let addWebFeedAccountID = "addWebFeedAccountID"
static let addWebFeedFolderName = "addWebFeedFolderName"
static let addFolderAccountID = "addFolderAccountID"
static let timelineSortDirection = "timelineSortDirection"
// iOS Defaults
static let userInterfaceColorPalette = "userInterfaceColorPalette"
static let timelineGroupByFeed = "timelineGroupByFeed"
static let refreshClearsReadArticles = "refreshClearsReadArticles"
static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let articleFullscreenAvailable = "articleFullscreenAvailable"
static let articleFullscreenEnabled = "articleFullscreenEnabled"
static let confirmMarkAllAsRead = "confirmMarkAllAsRead"
// macOS Defaults
static let windowState = "windowState"
static let sidebarFontSize = "sidebarFontSize"
static let timelineFontSize = "timelineFontSize"
static let detailFontSize = "detailFontSize"
static let openInBrowserInBackground = "openInBrowserInBackground"
static let importOPMLAccountID = "importOPMLAccountID"
static let exportOPMLAccountID = "exportOPMLAccountID"
static let defaultBrowserID = "defaultBrowserID"
static let checkForUpdatesAutomatically = "checkForUpdatesAutomatically"
static let downloadTestBuilds = "downloadTestBuild"
static let sendCrashLogs = "sendCrashLogs"
// Hidden macOS Defaults
static let showDebugMenu = "ShowDebugMenu"
static let timelineShowsSeparators = "CorreiaSeparators"
static let showTitleOnMainWindow = "KafasisTitleMode"
#if !MAC_APP_STORE
static let webInspectorEnabled = "WebInspectorEnabled"
static let webInspectorStartsAttached = "__WebInspectorPageGroupLevel1__.WebKit2InspectorStartsAttached"
#endif
}
static let isDeveloperBuild: Bool = {
private static let smallestFontSizeRawValue = FontSize.small.rawValue
private static let largestFontSizeRawValue = FontSize.veryLarge.rawValue
// MARK: Development Builds
let isDeveloperBuild: Bool = {
if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" {
return true
}
return false
}()
static let isFirstRun: Bool = {
if let _ = AppDefaults.shared.object(forKey: Key.firstRunDate) as? Date {
return false
}
firstRunDate = Date()
return true
}()
static var refreshInterval: RefreshInterval {
get {
let rawValue = UserDefaults.standard.integer(forKey: Key.refreshInterval)
return RefreshInterval(rawValue: rawValue) ?? RefreshInterval.everyHour
}
// MARK: First Run Details
var firstRunDate: Date? {
set {
UserDefaults.standard.set(newValue.rawValue, forKey: Key.refreshInterval)
AppDefaults.store.setValue(newValue, forKey: Key.firstRunDate)
objectWillChange.send()
}
get {
AppDefaults.store.object(forKey: Key.firstRunDate) as? Date
}
}
static var hideDockUnreadCount: Bool {
return bool(for: Key.hideDockUnreadCount)
// MARK: Refresh Interval
@AppStorage(wrappedValue: 4, Key.refreshInterval, store: store) var interval: Int {
didSet {
objectWillChange.send()
}
}
static var userInterfaceColorPalette: UserInterfaceColorPalette {
var refreshInterval: RefreshInterval {
RefreshInterval(rawValue: interval) ?? RefreshInterval.everyHour
}
// MARK: Dock Badge
@AppStorage(wrappedValue: false, Key.hideDockUnreadCount, store: store) var hideDockUnreadCount {
didSet {
objectWillChange.send()
}
}
// MARK: Color Palette
var userInterfaceColorPalette: UserInterfaceColorPalette {
get {
if let result = UserInterfaceColorPalette(rawValue: int(for: Key.userInterfaceColorPalette)) {
return result
if let palette = UserInterfaceColorPalette(rawValue: AppDefaults.store.integer(forKey: Key.userInterfaceColorPalette)) {
return palette
}
return .automatic
}
set {
setInt(for: Key.userInterfaceColorPalette, newValue.rawValue)
}
}
static var addWebFeedAccountID: String? {
get {
return string(for: Key.addWebFeedAccountID)
}
set {
setString(for: Key.addWebFeedAccountID, newValue)
AppDefaults.store.set(newValue.rawValue, forKey: Key.userInterfaceColorPalette)
objectWillChange.send()
}
}
static var addWebFeedFolderName: String? {
get {
return string(for: Key.addWebFeedFolderName)
}
set {
setString(for: Key.addWebFeedFolderName, newValue)
}
}
// MARK: Feeds & Folders
@AppStorage(Key.addWebFeedAccountID, store: store) var addWebFeedAccountID: String?
static var addFolderAccountID: String? {
get {
return string(for: Key.addFolderAccountID)
}
set {
setString(for: Key.addFolderAccountID, newValue)
}
}
@AppStorage(Key.addWebFeedFolderName, store: store) var addWebFeedFolderName: String?
static var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
@AppStorage(Key.addFolderAccountID, store: store) var addFolderAccountID: String?
@AppStorage(wrappedValue: false, Key.confirmMarkAllAsRead, store: store) var confirmMarkAllAsRead: Bool
// MARK: Extension Points
var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
get {
return UserDefaults.standard.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
return AppDefaults.store.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
}
set {
UserDefaults.standard.set(newValue, forKey: Key.activeExtensionPointIDs)
objectWillChange.send()
}
}
static var lastImageCacheFlushDate: Date? {
get {
return date(for: Key.lastImageCacheFlushDate)
}
// MARK: Image Cache
var lastImageCacheFlushDate: Date? {
set {
setDate(for: Key.lastImageCacheFlushDate, newValue)
AppDefaults.store.setValue(newValue, forKey: Key.lastImageCacheFlushDate)
objectWillChange.send()
}
}
static var timelineGroupByFeed: Bool {
get {
return bool(for: Key.timelineGroupByFeed)
}
set {
setBool(for: Key.timelineGroupByFeed, newValue)
}
}
static var refreshClearsReadArticles: Bool {
get {
return bool(for: Key.refreshClearsReadArticles)
}
set {
setBool(for: Key.refreshClearsReadArticles, newValue)
}
}
static var timelineSortDirection: ComparisonResult {
get {
return sortDirection(for: Key.timelineSortDirection)
}
set {
setSortDirection(for: Key.timelineSortDirection, newValue)
}
}
static var articleFullscreenAvailable: Bool {
get {
return bool(for: Key.articleFullscreenAvailable)
}
set {
setBool(for: Key.articleFullscreenAvailable, newValue)
}
}
static var articleFullscreenEnabled: Bool {
get {
return bool(for: Key.articleFullscreenEnabled)
}
set {
setBool(for: Key.articleFullscreenEnabled, newValue)
}
}
static var confirmMarkAllAsRead: Bool {
get {
return bool(for: Key.confirmMarkAllAsRead)
}
set {
setBool(for: Key.confirmMarkAllAsRead, newValue)
AppDefaults.store.object(forKey: Key.lastImageCacheFlushDate) as? Date
}
}
static var lastRefresh: Date? {
// MARK: Timeline
@AppStorage(wrappedValue: false, Key.timelineGroupByFeed, store: store) var timelineGroupByFeed: Bool
@AppStorage(wrappedValue: 3, Key.timelineNumberOfLines, store: store) var timelineNumberOfLines: Int {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: 40.0, Key.timelineIconSize, store: store) var timelineIconSize: Double {
didSet {
objectWillChange.send()
}
}
/// Set to `true` to sort oldest to newest, `false` for newest to oldest. Default is `false`.
@AppStorage(wrappedValue: false, Key.timelineSortDirection, store: store) var timelineSortDirection: Bool
// MARK: Refresh
@AppStorage(wrappedValue: false, Key.refreshClearsReadArticles, store: store) var refreshClearsReadArticles: Bool
// MARK: Articles
@AppStorage(wrappedValue: false, Key.articleFullscreenAvailable, store: store) var articleFullscreenAvailable: Bool
// MARK: Refresh
var lastRefresh: Date? {
set {
AppDefaults.store.setValue(newValue, forKey: Key.lastRefresh)
objectWillChange.send()
}
get {
return date(for: Key.lastRefresh)
AppDefaults.store.object(forKey: Key.lastRefresh) as? Date
}
}
// MARK: Window State
var windowState: [AnyHashable : Any]? {
get {
return AppDefaults.store.object(forKey: Key.windowState) as? [AnyHashable : Any]
}
set {
setDate(for: Key.lastRefresh, newValue)
UserDefaults.standard.set(newValue, forKey: Key.windowState)
objectWillChange.send()
}
}
static var timelineNumberOfLines: Int {
@AppStorage(wrappedValue: false, Key.openInBrowserInBackground, store: store) var openInBrowserInBackground: Bool {
didSet {
objectWillChange.send()
}
}
var sidebarFontSize: FontSize {
get {
return int(for: Key.timelineNumberOfLines)
return fontSize(for: Key.sidebarFontSize)
}
set {
setInt(for: Key.timelineNumberOfLines, newValue)
AppDefaults.store.set(newValue.rawValue, forKey: Key.sidebarFontSize)
objectWillChange.send()
}
}
static var timelineIconSize: IconSize {
var timelineFontSize: FontSize {
get {
let rawValue = AppDefaults.shared.integer(forKey: Key.timelineIconSize)
return IconSize(rawValue: rawValue) ?? IconSize.medium
return fontSize(for: Key.timelineFontSize)
}
set {
AppDefaults.shared.set(newValue.rawValue, forKey: Key.timelineIconSize)
AppDefaults.store.set(newValue.rawValue, forKey: Key.timelineFontSize)
objectWillChange.send()
}
}
static func registerDefaults() {
let defaults: [String : Any] = [Key.userInterfaceColorPalette: UserInterfaceColorPalette.automatic.rawValue,
Key.timelineGroupByFeed: false,
Key.refreshClearsReadArticles: false,
Key.timelineNumberOfLines: 2,
Key.timelineIconSize: IconSize.medium.rawValue,
Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue,
Key.articleFullscreenAvailable: false,
Key.articleFullscreenEnabled: false,
Key.confirmMarkAllAsRead: true]
AppDefaults.shared.register(defaults: defaults)
}
}
private extension AppDefaults {
static var firstRunDate: Date? {
var detailFontSize: FontSize {
get {
return date(for: Key.firstRunDate)
return fontSize(for: Key.detailFontSize)
}
set {
setDate(for: Key.firstRunDate, newValue)
AppDefaults.store.set(newValue.rawValue, forKey: Key.detailFontSize)
objectWillChange.send()
}
}
static func string(for key: String) -> String? {
return AppDefaults.shared.string(forKey: key)
}
static func setString(for key: String, _ value: String?) {
AppDefaults.shared.set(value, forKey: key)
}
static func bool(for key: String) -> Bool {
return AppDefaults.shared.bool(forKey: key)
}
static func setBool(for key: String, _ flag: Bool) {
AppDefaults.shared.set(flag, forKey: key)
}
static func int(for key: String) -> Int {
return AppDefaults.shared.integer(forKey: key)
}
static func setInt(for key: String, _ x: Int) {
AppDefaults.shared.set(x, forKey: key)
}
static func date(for key: String) -> Date? {
return AppDefaults.shared.object(forKey: key) as? Date
}
static func setDate(for key: String, _ date: Date?) {
AppDefaults.shared.set(date, forKey: key)
}
static func sortDirection(for key:String) -> ComparisonResult {
let rawInt = int(for: key)
if rawInt == ComparisonResult.orderedAscending.rawValue {
return .orderedAscending
@AppStorage(Key.importOPMLAccountID, store: store) var importOPMLAccountID: String? {
didSet {
objectWillChange.send()
}
return .orderedDescending
}
static func setSortDirection(for key: String, _ value: ComparisonResult) {
if value == .orderedAscending {
setInt(for: key, ComparisonResult.orderedAscending.rawValue)
@AppStorage(Key.exportOPMLAccountID, store: store) var exportOPMLAccountID: String? {
didSet {
objectWillChange.send()
}
else {
setInt(for: key, ComparisonResult.orderedDescending.rawValue)
}
@AppStorage(Key.defaultBrowserID, store: store) var defaultBrowserID: String? {
didSet {
objectWillChange.send()
}
}
@AppStorage(Key.showTitleOnMainWindow, store: store) var showTitleOnMainWindow: Bool? {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.showDebugMenu, store: store) var showDebugMenu: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.timelineShowsSeparators, store: store) var timelineShowsSeparators: Bool {
didSet {
objectWillChange.send()
}
}
#if !MAC_APP_STORE
@AppStorage(wrappedValue: false, Key.webInspectorEnabled, store: store) var webInspectorEnabled: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.webInspectorStartsAttached, store: store) var webInspectorStartsAttached: Bool {
didSet {
objectWillChange.send()
}
}
#endif
@AppStorage(wrappedValue: true, Key.checkForUpdatesAutomatically, store: store) var checkForUpdatesAutomatically: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: false, Key.downloadTestBuilds, store: store) var downloadTestBuilds: Bool {
didSet {
objectWillChange.send()
}
}
@AppStorage(wrappedValue: true, Key.sendCrashLogs, store: store) var sendCrashLogs: Bool {
didSet {
objectWillChange.send()
}
}
}
extension AppDefaults {
func isFirstRun() -> Bool {
if let _ = AppDefaults.store.object(forKey: Key.firstRunDate) as? Date {
return false
}
firstRunDate = Date()
return true
}
func fontSize(for key: String) -> FontSize {
// Punted till after 1.0.
return .medium
}
}

View File

@@ -13,13 +13,13 @@ struct MainApp: App {
#if os(macOS)
@NSApplicationDelegateAdaptor(AppDelegate.self) private var delegate
let preferences = MacPreferences()
#endif
#if os(iOS)
@UIApplicationDelegateAdaptor(AppDelegate.self) private var delegate
#endif
@StateObject private var sceneModel = SceneModel()
@StateObject private var defaults = AppDefaults.shared
@SceneBuilder var body: some Scene {
#if os(macOS)
@@ -134,7 +134,7 @@ struct MainApp: App {
.padding()
.frame(width: 500)
.navigationTitle("Preferences")
.environmentObject(preferences)
.environmentObject(defaults)
}
.windowToolbarStyle(UnifiedWindowToolbarStyle())

View File

@@ -22,6 +22,7 @@ struct SceneNavigationView: View {
#else
if horizontalSizeClass == .compact {
CompactSidebarContainerView()
} else {
RegularSidebarContainerView()
}
@@ -46,6 +47,7 @@ struct SceneNavigationView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
#endif
}
}
}

View File

@@ -71,9 +71,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppDefaults.registerDefaults()
//AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.isFirstRun
let isFirstRun = AppDefaults.shared.isFirstRun()
if isFirstRun {
os_log("Is first run.", log: log, type: .info)
}
@@ -139,7 +139,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
@objc func accountRefreshDidFinish(_ note: Notification) {
AppDefaults.lastRefresh = Date()
AppDefaults.shared.lastRefresh = Date()
}
// MARK: - API
@@ -163,7 +163,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// extensionFeedAddRequestFile.resume()
syncTimer?.update()
if let lastRefresh = AppDefaults.lastRefresh {
if let lastRefresh = AppDefaults.shared.lastRefresh {
if Date() > lastRefresh.addingTimeInterval(15 * 60) {
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
} else {

View File

@@ -0,0 +1,72 @@
//
// AppSettings.swift
// Multiplatform iOS
//
// Created by Stuart Breckenridge on 1/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
enum ColorPalette: Int, CustomStringConvertible, CaseIterable {
case automatic = 0
case light = 1
case dark = 2
var description: String {
switch self {
case .automatic:
return NSLocalizedString("Automatic", comment: "Automatic")
case .light:
return NSLocalizedString("Light", comment: "Light")
case .dark:
return NSLocalizedString("Dark", comment: "Dark")
}
}
}
class AppSettings: ObservableObject {
struct Key {
static let userInterfaceColorPalette = "userInterfaceColorPalette"
static let activeExtensionPointIDs = "activeExtensionPointIDs"
static let lastImageCacheFlushDate = "lastImageCacheFlushDate"
static let firstRunDate = "firstRunDate"
static let timelineGroupByFeed = "timelineGroupByFeed"
static let refreshClearsReadArticles = "refreshClearsReadArticles"
static let timelineNumberOfLines = "timelineNumberOfLines"
static let timelineIconSize = "timelineIconSize"
static let timelineSortDirection = "timelineSortDirection"
static let articleFullscreenAvailable = "articleFullscreenAvailable"
static let articleFullscreenEnabled = "articleFullscreenEnabled"
static let confirmMarkAllAsRead = "confirmMarkAllAsRead"
static let lastRefresh = "lastRefresh"
static let addWebFeedAccountID = "addWebFeedAccountID"
static let addWebFeedFolderName = "addWebFeedFolderName"
static let addFolderAccountID = "addFolderAccountID"
}
static let isDeveloperBuild: Bool = {
if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" {
return true
}
return false
}()
static let isFirstRun: Bool = {
if let _ = AppDefaults.shared.object(forKey: Key.firstRunDate) as? Date {
return false
}
firstRunDate = Date()
return true
}()
}

View File

@@ -56,7 +56,8 @@ struct SettingsView: View {
let sortedAccounts = AccountManager.shared.sortedAccounts
@Environment(\.presentationMode) var presentationMode
@ObservedObject private var viewModel = SettingsViewModel()
@StateObject private var viewModel = SettingsViewModel()
@StateObject private var settings = AppDefaults.shared
var body: some View {
NavigationView {
@@ -128,11 +129,11 @@ struct SettingsView: View {
var timeline: some View {
Section(header: Text("Timeline"), content: {
Toggle("Sort Oldest to Newest", isOn: .constant(true))
Toggle("Group by Feed", isOn: .constant(true))
Toggle("Refresh to Clear Read Articles", isOn: .constant(true))
Toggle("Sort Oldest to Newest", isOn: $settings.timelineSortDirection)
Toggle("Group by Feed", isOn: $settings.timelineGroupByFeed)
Toggle("Refresh to Clear Read Articles", isOn: $settings.refreshClearsReadArticles)
NavigationLink(
destination: EmptyView(),
destination: TimelineLayoutView().environmentObject(settings),
label: {
Text("Timeline Layout")
})

View File

@@ -0,0 +1,82 @@
//
// TimelineLayoutView.swift
// Multiplatform iOS
//
// Created by Stuart Breckenridge on 1/7/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
struct TimelineLayoutView: View {
@EnvironmentObject private var appSettings: AppDefaults
private let sampleTitle = "Lorem dolor sed viverra ipsum. Gravida rutrum quisque non tellus. Rutrum tellus pellentesque eu tincidunt tortor. Sed blandit libero volutpat sed cras ornare. Et netus et malesuada fames ac. Ultrices eros in cursus turpis massa tincidunt dui ut ornare. Lacus sed viverra tellus in. Sollicitudin ac orci phasellus egestas. Purus in mollis nunc sed. Sollicitudin ac orci phasellus egestas tellus rutrum tellus pellentesque. Interdum consectetur libero id faucibus nisl tincidunt eget."
var body: some View {
VStack(spacing: 0) {
List {
Section(header: Text("Icon Size"), content: {
iconSize
})
Section(header: Text("Number of Lines"), content: {
numberOfLines
}) }
.listStyle(InsetGroupedListStyle())
Divider()
timelineRowPreview.padding()
Divider()
}
.navigationBarTitle("Timeline Layout")
}
var iconSize: some View {
Slider(value: $appSettings.timelineIconSize, in: 20...60, step: 10, minimumValueLabel: Text("Small"), maximumValueLabel: Text("Large"), label: {
Text(String(appSettings.timelineIconSize))
})
}
var numberOfLines: some View {
Stepper(value: $appSettings.timelineNumberOfLines, in: 1...5, label: {
Text("Title")
})
}
var timelineRowPreview: some View {
HStack(alignment: .top) {
Image(systemName: "circle.fill")
.resizable()
.frame(width: 10, height: 10, alignment: .top)
.foregroundColor(.accentColor)
Image(systemName: "paperplane.circle")
.resizable()
.frame(width: CGFloat(appSettings.timelineIconSize), height: CGFloat(appSettings.timelineIconSize), alignment: .top)
.foregroundColor(.accentColor)
VStack(alignment: .leading, spacing: 4) {
Text(sampleTitle)
.font(.headline)
.lineLimit(appSettings.timelineNumberOfLines)
HStack {
Text("Feed Name")
.foregroundColor(.secondary)
.font(.footnote)
Spacer()
Text("10:31")
.font(.footnote)
.foregroundColor(.secondary)
}
}
}
}
}
struct TimelineLayout_Previews: PreviewProvider {
static var previews: some View {
TimelineLayoutView()
}
}

View File

@@ -133,8 +133,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
}
#endif
AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.isFirstRun
//AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.shared.isFirstRun()
if isFirstRun {
os_log(.debug, log: log, "Is first run.")
}
@@ -245,7 +245,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
// MARK: - Dock Badge
@objc func updateDockBadge() {
let label = unreadCount > 0 && !AppDefaults.hideDockUnreadCount ? "\(unreadCount)" : ""
let label = unreadCount > 0 && !AppDefaults.shared.hideDockUnreadCount ? "\(unreadCount)" : ""
NSApplication.shared.dockTile.badgeLabel = label
}

View File

@@ -32,19 +32,19 @@ struct MacPreferenceViewModel {
struct MacPreferencesView: View {
@EnvironmentObject var preferences: MacPreferences
@EnvironmentObject var defaults: AppDefaults
@State private var viewModel = MacPreferenceViewModel()
var body: some View {
VStack {
if viewModel.currentPreferencePane == .general {
AnyView(GeneralPreferencesView())
AnyView(GeneralPreferencesView().environmentObject(defaults))
}
else if viewModel.currentPreferencePane == .accounts {
AnyView(AccountsPreferencesView())
AnyView(AccountsPreferencesView().environmentObject(defaults))
}
else {
AnyView(AdvancedPreferencesView(preferences: preferences))
AnyView(AdvancedPreferencesView().environmentObject(defaults))
}
}
.toolbar {

View File

@@ -1,96 +0,0 @@
//
// MacPreferences.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
enum FontSize: Int {
case small = 0
case medium = 1
case large = 2
case veryLarge = 3
}
/// The `MacPreferences` object stores all macOS specific user preferences.
class MacPreferences: ObservableObject {
private struct AppKeys {
static let refreshInterval = "refreshInterval"
static let openInBackground = "openInBrowserInBackground"
static let showUnreadCountInDock = "showUnreadCountInDock"
static let checkForUpdatesAutomatically = "checkForAppUpdates"
static let downloadTestBuilds = "downloadTestBuilds"
static let sendCrashLogs = "sendCrashLogs"
}
// Refresh Interval
public let refreshIntervals:[String] = RefreshFrequencies.allCases.map({ $0.description })
@AppStorage(wrappedValue: 0, AppKeys.refreshInterval) var refreshFrequency {
didSet {
objectWillChange.send()
}
}
// Open in background
@AppStorage(wrappedValue: false, AppKeys.openInBackground) var openInBackground {
didSet {
objectWillChange.send()
}
}
// Unread Count in Dock
@AppStorage(wrappedValue: true, AppKeys.showUnreadCountInDock) var showUnreadCountInDock {
didSet {
objectWillChange.send()
}
}
// Check for App Updates
@AppStorage(wrappedValue: true, AppKeys.checkForUpdatesAutomatically) var checkForUpdatesAutomatically {
didSet {
objectWillChange.send()
}
}
// Test builds
@AppStorage(wrappedValue: false, AppKeys.downloadTestBuilds) var downloadTestBuilds {
didSet {
objectWillChange.send()
}
}
// Crash Logs
@AppStorage(wrappedValue: false, AppKeys.sendCrashLogs) var sendCrashLogs {
didSet {
objectWillChange.send()
}
}
}
enum RefreshFrequencies: CaseIterable, CustomStringConvertible {
case refreshEvery10Mins, refreshEvery20Mins, refreshHourly, refreshEvery2Hours, refreshEvery4Hours, refreshEvery8Hours, none
var description: String {
switch self {
case .refreshEvery10Mins:
return "Every 10 minutes"
case .refreshEvery20Mins:
return "Every 20 minutes"
case .refreshHourly:
return "Every hour"
case .refreshEvery2Hours:
return "Every 2 hours"
case .refreshEvery4Hours:
return "Every 4 hours"
case .refreshEvery8Hours:
return "Every 8 hours"
case .none:
return "Manually"
}
}
}

View File

@@ -9,7 +9,7 @@ import SwiftUI
struct AdvancedPreferencesView: View {
@StateObject var preferences: MacPreferences
@EnvironmentObject private var preferences: AppDefaults
var body: some View {
VStack {

View File

@@ -0,0 +1,33 @@
//
// GeneralPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct GeneralPreferencesView: View {
@EnvironmentObject private var defaults: AppDefaults
var body: some View {
VStack {
Form {
Picker("Refresh Feeds",
selection: $defaults.interval,
content: {
ForEach(RefreshInterval.allCases, content: { interval in
Text(interval.description()).tag(interval.rawValue)
})
}).frame(width: 300, alignment: .center)
Toggle("Open webpages in background in browser", isOn: $defaults.openInBrowserInBackground)
Toggle("Hide Unread Count in Dock", isOn: $defaults.hideDockUnreadCount)
}
Spacer()
}.frame(width: 300, alignment: .center)
}
}

View File

@@ -1,33 +0,0 @@
//
// GeneralPreferencesView.swift
// macOS
//
// Created by Stuart Breckenridge on 27/6/20.
//
import SwiftUI
struct GeneralPreferencesView: View {
@ObservedObject private var preferences = MacPreferences()
var body: some View {
VStack {
Form {
Picker("Refresh Feeds",
selection: $preferences.refreshFrequency,
content: {
ForEach(0..<preferences.refreshIntervals.count, content: {
Text(preferences.refreshIntervals[$0])
})
}).frame(width: 300, alignment: .center)
Toggle("Open webpages in background in browser", isOn: $preferences.openInBackground)
Toggle("Show Unread Count in Dock", isOn: $preferences.showUnreadCountInDock)
}
Spacer()
}.frame(width: 300, alignment: .center)
}
}

View File

@@ -11,15 +11,19 @@
172199ED24AB2E0100A31D04 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199EC24AB2E0100A31D04 /* SafariView.swift */; };
172199EF24AB372D00A31D04 /* VisualEffectBlur.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199EE24AB372D00A31D04 /* VisualEffectBlur.swift */; };
172199F124AB716900A31D04 /* SidebarToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172199F024AB716900A31D04 /* SidebarToolbar.swift */; };
1729528E24AA1A4900D65E66 /* MacPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729526C24AA1A4900D65E66 /* MacPreferences.swift */; };
1729529324AA1CAA00D65E66 /* AccountsPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529024AA1CAA00D65E66 /* AccountsPreferencesView.swift */; };
1729529424AA1CAA00D65E66 /* AdvancedPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529124AA1CAA00D65E66 /* AdvancedPreferencesView.swift */; };
1729529524AA1CAA00D65E66 /* GeneralPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */; };
1729529724AA1CD000D65E66 /* MacPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529624AA1CD000D65E66 /* MacPreferencesView.swift */; };
1729529B24AA1FD200D65E66 /* MacSearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529A24AA1FD200D65E66 /* MacSearchField.swift */; };
172952B024AA287100D65E66 /* CompactSidebarContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172952AF24AA287100D65E66 /* CompactSidebarContainerView.swift */; };
175942AA24AD533200585066 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
175942AB24AD533200585066 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppDefaults.swift */; };
1776E88F24AC5F8A00E78166 /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1776E88D24AC5F8A00E78166 /* AppDefaults.swift */; };
179DB1DFBCF9177104B12E0F /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
179DB3CE822BFCC2D774D9F4 /* AccountsNewsBlurWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */; };
17B223DC24AC24D2001E4592 /* TimelineLayoutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */; };
3B3A32A5238B820900314204 /* FeedWranglerAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */; };
3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */; };
3B826DCC2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; };
@@ -496,8 +500,6 @@
51E4992424A8098400B667CB /* SmartFeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84AD1EB92031649C00BC20B7 /* SmartFeedPasteboardWriter.swift */; };
51E4992624A80AAB00B667CB /* AppAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992524A80AAB00B667CB /* AppAssets.swift */; };
51E4992724A80AAB00B667CB /* AppAssets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992524A80AAB00B667CB /* AppAssets.swift */; };
51E4992924A866F000B667CB /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992824A866F000B667CB /* AppDefaults.swift */; };
51E4992A24A866F000B667CB /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E4992824A866F000B667CB /* AppDefaults.swift */; };
51E4992B24A8676300B667CB /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
51E4992C24A8676300B667CB /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
51E4992D24A8676300B667CB /* FetchRequestOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84CAFCAE22BC8C35007694F0 /* FetchRequestOperation.swift */; };
@@ -518,9 +520,7 @@
51E4993E24A870F900B667CB /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; };
51E4994024A8713B00B667CB /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; };
51E4994124A8713B00B667CB /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
51E4994224A8713C00B667CB /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; };
51E4994424A8713C00B667CB /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
51E4994524A872AD00B667CB /* org.sparkle-project.Downloader.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 65ED42BC235E71B40081F399 /* org.sparkle-project.Downloader.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
51E4994624A872AD00B667CB /* org.sparkle-project.InstallerConnection.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 65ED42B8235E71B40081F399 /* org.sparkle-project.InstallerConnection.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
51E4994724A872AD00B667CB /* org.sparkle-project.InstallerLauncher.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = 65ED42B6235E71B40081F399 /* org.sparkle-project.InstallerLauncher.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
@@ -1704,14 +1704,15 @@
172199EC24AB2E0100A31D04 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
172199EE24AB372D00A31D04 /* VisualEffectBlur.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VisualEffectBlur.swift; sourceTree = "<group>"; };
172199F024AB716900A31D04 /* SidebarToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarToolbar.swift; sourceTree = "<group>"; };
1729526C24AA1A4900D65E66 /* MacPreferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacPreferences.swift; sourceTree = "<group>"; };
1729529024AA1CAA00D65E66 /* AccountsPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsPreferencesView.swift; sourceTree = "<group>"; };
1729529124AA1CAA00D65E66 /* AdvancedPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdvancedPreferencesView.swift; sourceTree = "<group>"; };
1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesView.swift; sourceTree = "<group>"; };
1729529624AA1CD000D65E66 /* MacPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacPreferencesView.swift; sourceTree = "<group>"; };
1729529A24AA1FD200D65E66 /* MacSearchField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacSearchField.swift; sourceTree = "<group>"; };
172952AF24AA287100D65E66 /* CompactSidebarContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompactSidebarContainerView.swift; sourceTree = "<group>"; };
1776E88D24AC5F8A00E78166 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefaults.swift; sourceTree = "<group>"; };
179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsNewsBlurWindowController.swift; sourceTree = "<group>"; };
17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineLayoutView.swift; sourceTree = "<group>"; };
3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountViewController.swift; sourceTree = "<group>"; };
3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = "<group>"; };
3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = "<group>"; };
@@ -1932,7 +1933,6 @@
51E4989824A8067000B667CB /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
51E498B224A806AA00B667CB /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
51E4992524A80AAB00B667CB /* AppAssets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = "<group>"; };
51E4992824A866F000B667CB /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefaults.swift; sourceTree = "<group>"; };
51E4993924A8708800B667CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
51E4993B24A8709900B667CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
51E4995824A873F900B667CB /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
@@ -2361,6 +2361,7 @@
isa = PBXGroup;
children = (
172199C824AB228900A31D04 /* SettingsView.swift */,
17B223B924AC24A8001E4592 /* Submenus */,
);
path = Settings;
sourceTree = "<group>";
@@ -2368,29 +2369,28 @@
1729528F24AA1A4F00D65E66 /* Preferences */ = {
isa = PBXGroup;
children = (
1729529824AA1CD900D65E66 /* Model */,
1729529924AA1CE100D65E66 /* Views */,
1729529624AA1CD000D65E66 /* MacPreferencesView.swift */,
1729529924AA1CE100D65E66 /* View */,
);
path = Preferences;
sourceTree = "<group>";
};
1729529824AA1CD900D65E66 /* Model */ = {
1729529924AA1CE100D65E66 /* View */ = {
isa = PBXGroup;
children = (
1729526C24AA1A4900D65E66 /* MacPreferences.swift */,
);
path = Model;
sourceTree = "<group>";
};
1729529924AA1CE100D65E66 /* Views */ = {
isa = PBXGroup;
children = (
1729529624AA1CD000D65E66 /* MacPreferencesView.swift */,
1729529024AA1CAA00D65E66 /* AccountsPreferencesView.swift */,
1729529124AA1CAA00D65E66 /* AdvancedPreferencesView.swift */,
1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */,
);
path = Views;
path = View;
sourceTree = "<group>";
};
17B223B924AC24A8001E4592 /* Submenus */ = {
isa = PBXGroup;
children = (
17B223DB24AC24D2001E4592 /* TimelineLayoutView.swift */,
);
path = Submenus;
sourceTree = "<group>";
};
510289CE2451BA1E00426DDF /* Twitter */ = {
@@ -2723,7 +2723,7 @@
isa = PBXGroup;
children = (
51E4992524A80AAB00B667CB /* AppAssets.swift */,
51E4992824A866F000B667CB /* AppDefaults.swift */,
1776E88D24AC5F8A00E78166 /* AppDefaults.swift */,
51E4995824A873F900B667CB /* ErrorHandler.swift */,
51C0513624A77DF700194D5E /* MainApp.swift */,
51E499D724A912C200B667CB /* SceneModel.swift */,
@@ -3925,46 +3925,46 @@
TargetAttributes = {
51314636235A7BBE00387FDC = {
CreatedOnToolsVersion = 11.2;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
LastSwiftMigration = 1120;
ProvisioningStyle = Automatic;
};
513C5CE5232571C2003D4054 = {
CreatedOnToolsVersion = 11.0;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
};
518B2ED12351B3DD00400001 = {
CreatedOnToolsVersion = 11.2;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
TestTargetID = 840D617B2029031C009BC708;
};
51C0513C24A77DF800194D5E = {
CreatedOnToolsVersion = 12.0;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
};
51C0514324A77DF800194D5E = {
CreatedOnToolsVersion = 12.0;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
};
6581C73220CED60000F4AD34 = {
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
};
65ED3FA2235DEF6C0081F399 = {
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
};
65ED4090235DEF770081F399 = {
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
};
840D617B2029031C009BC708 = {
CreatedOnToolsVersion = 9.3;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.BackgroundModes = {
@@ -3974,7 +3974,7 @@
};
849C645F1ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.HardenedRuntime = {
@@ -3984,7 +3984,7 @@
};
849C64701ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = FQLBNX3GP7;
ProvisioningStyle = Automatic;
TestTargetID = 849C645F1ED37A5D003D8FC0;
};
@@ -4753,7 +4753,6 @@
51E4995924A873F900B667CB /* ErrorHandler.swift in Sources */,
51392D1B24AC19A000BE0D35 /* SidebarExpandedContainers.swift in Sources */,
51E4992F24A8676400B667CB /* ArticleArray.swift in Sources */,
51E4994424A8713C00B667CB /* RefreshInterval.swift in Sources */,
51E498F824A8085D00B667CB /* UnreadFeed.swift in Sources */,
51E4996A24A8762D00B667CB /* ExtractedArticle.swift in Sources */,
51919FF124AB864A00541E64 /* TimelineModel.swift in Sources */,
@@ -4806,12 +4805,12 @@
51E4992624A80AAB00B667CB /* AppAssets.swift in Sources */,
514E6C0624AD2B5F00AC6F6E /* Image-Extensions.swift in Sources */,
51E4995624A8734D00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,
51E4992924A866F000B667CB /* AppDefaults.swift in Sources */,
51E4996824A8760C00B667CB /* ArticleStyle.swift in Sources */,
51E4990024A808BB00B667CB /* FaviconGenerator.swift in Sources */,
51E4997124A8764C00B667CB /* ActivityType.swift in Sources */,
51E4991E24A8094300B667CB /* RSImage-AppIcons.swift in Sources */,
51E499D824A912C200B667CB /* SceneModel.swift in Sources */,
51919FB324AAB97900541E64 /* FeedImageLoader.swift in Sources */,
51919FB324AAB97900541E64 /* FeedIconImageLoader.swift in Sources */,
51E4991324A808FB00B667CB /* AddWebFeedDefaultContainer.swift in Sources */,
51E4993C24A8709900B667CB /* AppDelegate.swift in Sources */,
@@ -4825,6 +4824,7 @@
51919FAC24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,
51E4991924A8090A00B667CB /* CacheCleaner.swift in Sources */,
51E498F724A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
175942AA24AD533200585066 /* RefreshInterval.swift in Sources */,
51E4993524A867E800B667CB /* AppNotifications.swift in Sources */,
51C0515E24A77DF800194D5E /* MainApp.swift in Sources */,
51919FF724AB8B7700541E64 /* TimelineView.swift in Sources */,
@@ -4832,6 +4832,7 @@
51E4991524A808FF00B667CB /* ArticleStringFormatter.swift in Sources */,
51919FEE24AB85E400541E64 /* TimelineContainerView.swift in Sources */,
51E4995724A8734D00B667CB /* ExtensionPoint.swift in Sources */,
1776E88E24AC5F8A00E78166 /* AppDefaults.swift in Sources */,
51E4991124A808DE00B667CB /* SmallIconProvider.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -4874,11 +4875,11 @@
51E49A0124A91FC100B667CB /* RegularSidebarContainerView.swift in Sources */,
51E4995B24A875D500B667CB /* ArticlePasteboardWriter.swift in Sources */,
51E4993424A867E700B667CB /* UserInfoKey.swift in Sources */,
1776E88F24AC5F8A00E78166 /* AppDefaults.swift in Sources */,
1729529724AA1CD000D65E66 /* MacPreferencesView.swift in Sources */,
51E4994C24A8734C00B667CB /* RedditFeedProvider-Extensions.swift in Sources */,
1729529324AA1CAA00D65E66 /* AccountsPreferencesView.swift in Sources */,
51919FAD24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,
51E4994124A8713B00B667CB /* RefreshInterval.swift in Sources */,
51E498C924A8085D00B667CB /* PseudoFeed.swift in Sources */,
51E498FC24A808BA00B667CB /* FaviconURLFinder.swift in Sources */,
51E4991C24A8092000B667CB /* NSAttributedString+NetNewsWire.swift in Sources */,
@@ -4891,6 +4892,7 @@
51E49A0424A91FF600B667CB /* SceneNavigationView.swift in Sources */,
51E498CC24A8085D00B667CB /* SearchFeedDelegate.swift in Sources */,
51E498C824A8085D00B667CB /* SmartFeedsController.swift in Sources */,
175942AB24AD533200585066 /* RefreshInterval.swift in Sources */,
51E4992C24A8676300B667CB /* ArticleSorter.swift in Sources */,
514E6C0A24AD39AD00AC6F6E /* ArticleIconImageLoader.swift in Sources */,
51E4995024A8734C00B667CB /* ExtensionPoint.swift in Sources */,
@@ -4904,7 +4906,6 @@
51E4995A24A873F900B667CB /* ErrorHandler.swift in Sources */,
51E4991F24A8094300B667CB /* RSImage-AppIcons.swift in Sources */,
51E4991224A808FB00B667CB /* AddWebFeedDefaultContainer.swift in Sources */,
1729528E24AA1A4900D65E66 /* MacPreferences.swift in Sources */,
51E4993E24A870F900B667CB /* UserNotificationManager.swift in Sources */,
51E4992E24A8676300B667CB /* FetchRequestQueue.swift in Sources */,
51E498CF24A8085D00B667CB /* SmartFeed.swift in Sources */,
@@ -4916,7 +4917,6 @@
51E498FE24A808BA00B667CB /* FaviconDownloader.swift in Sources */,
51919FA724AA64B000541E64 /* SidebarView.swift in Sources */,
51E498FD24A808BA00B667CB /* ColorHash.swift in Sources */,
51E4992A24A866F000B667CB /* AppDefaults.swift in Sources */,
51E4991824A8090A00B667CB /* CacheCleaner.swift in Sources */,
51E498CD24A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
51E4996124A875F400B667CB /* ArticleRenderer.swift in Sources */,

View File

@@ -38,7 +38,7 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
let activeExtensionPointTypes = activeExtensionPoints.keys.compactMap({ ObjectIdentifier($0.extensionPointType) })
var available = [ExtensionPoint.Type]()
for possibleExtensionPointType in possibleExtensionPointTypes {
if !(AppDefaults.isDeveloperBuild && possibleExtensionPointType.isDeveloperBuildRestricted) {
if !(AppDefaults.shared.isDeveloperBuild && possibleExtensionPointType.isDeveloperBuildRestricted) {
if possibleExtensionPointType.isSinglton {
if !activeExtensionPointTypes.contains(ObjectIdentifier(possibleExtensionPointType)) {
available.append(possibleExtensionPointType)
@@ -109,7 +109,7 @@ final class ExtensionPointManager: FeedProviderManagerDelegate {
private extension ExtensionPointManager {
func loadExtensionPoints() {
if let extensionPointUserInfos = AppDefaults.activeExtensionPointIDs {
if let extensionPointUserInfos = AppDefaults.shared.activeExtensionPointIDs {
for extensionPointUserInfo in extensionPointUserInfos {
if let extensionPointID = ExtensionPointIdentifer(userInfo: extensionPointUserInfo) {
activeExtensionPoints[extensionPointID] = extensionPoint(for: extensionPointID)
@@ -119,7 +119,7 @@ private extension ExtensionPointManager {
}
func saveExtensionPointIDs() {
AppDefaults.activeExtensionPointIDs = activeExtensionPoints.keys.map({ $0.userInfo })
AppDefaults.shared.activeExtensionPointIDs = activeExtensionPoints.keys.map({ $0.userInfo })
NotificationCenter.default.post(name: .ActiveExtensionPointsDidChange, object: nil, userInfo: nil)
}

View File

@@ -13,8 +13,8 @@ struct AddWebFeedDefaultContainer {
static var defaultContainer: Container? {
if let accountID = AppDefaults.addWebFeedAccountID, let account = AccountManager.shared.activeAccounts.first(where: { $0.accountID == accountID }) {
if let folderName = AppDefaults.addWebFeedFolderName, let folder = account.existingFolder(withDisplayName: folderName) {
if let accountID = AppDefaults.shared.addWebFeedAccountID, let account = AccountManager.shared.activeAccounts.first(where: { $0.accountID == accountID }) {
if let folderName = AppDefaults.shared.addWebFeedFolderName, let folder = account.existingFolder(withDisplayName: folderName) {
return folder
} else {
return substituteContainerIfNeeded(account: account)
@@ -28,11 +28,11 @@ struct AddWebFeedDefaultContainer {
}
static func saveDefaultContainer(_ container: Container) {
AppDefaults.addWebFeedAccountID = container.account?.accountID
AppDefaults.shared.addWebFeedAccountID = container.account?.accountID
if let folder = container as? Folder {
AppDefaults.addWebFeedFolderName = folder.nameForDisplay
AppDefaults.shared.addWebFeedFolderName = folder.nameForDisplay
} else {
AppDefaults.addWebFeedFolderName = nil
AppDefaults.shared.addWebFeedFolderName = nil
}
}

View File

@@ -15,8 +15,8 @@ struct CacheCleaner {
static func purgeIfNecessary() {
guard let flushDate = AppDefaults.lastImageCacheFlushDate else {
AppDefaults.lastImageCacheFlushDate = Date()
guard let flushDate = AppDefaults.shared.lastImageCacheFlushDate else {
AppDefaults.shared.lastImageCacheFlushDate = Date()
return
}
@@ -41,7 +41,7 @@ struct CacheCleaner {
}
}
AppDefaults.lastImageCacheFlushDate = Date()
AppDefaults.shared.lastImageCacheFlushDate = Date()
}
}

View File

@@ -20,7 +20,7 @@ class AccountRefreshTimer {
func fireOldTimer() {
if let timer = internalTimer {
if timer.fireDate < Date() {
if AppDefaults.refreshInterval != .manually {
if AppDefaults.shared.refreshInterval != .manually {
timedRefresh(nil)
}
}
@@ -42,7 +42,7 @@ class AccountRefreshTimer {
return
}
let refreshInterval = AppDefaults.refreshInterval
let refreshInterval = AppDefaults.shared.refreshInterval
if refreshInterval == .manually {
invalidate()
return

View File

@@ -8,7 +8,7 @@
import Foundation
enum RefreshInterval: Int, CaseIterable {
enum RefreshInterval: Int, CaseIterable, Identifiable {
case manually = 1
case every10Minutes = 2
case every30Minutes = 3
@@ -36,6 +36,8 @@ enum RefreshInterval: Int, CaseIterable {
}
}
var id: String { description() }
func description() -> String {
switch self {
case .manually:

View File

@@ -22,7 +22,7 @@ class AddFolderViewController: UITableViewController, AddContainerViewController
private var accounts: [Account]! {
didSet {
if let predefinedAccount = accounts.first(where: { $0.accountID == AppDefaults.addFolderAccountID }) {
if let predefinedAccount = accounts.first(where: { $0.accountID == AppDefaults.shared.addFolderAccountID }) {
selectedAccount = predefinedAccount
} else {
selectedAccount = accounts[0]
@@ -69,7 +69,7 @@ class AddFolderViewController: UITableViewController, AddContainerViewController
}
private func didSelect(_ account: Account) {
AppDefaults.addFolderAccountID = account.accountID
AppDefaults.shared.addFolderAccountID = account.accountID
selectedAccount = account
}

View File

@@ -26,9 +26,12 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
}
struct AppDefaults {
class AppDefaults {
static var shared: UserDefaults = {
static let shared = AppDefaults()
private init() {}
static var store: UserDefaults = {
let appIdentifierPrefix = Bundle.main.object(forInfoDictionaryKey: "AppIdentifierPrefix") as! String
let suiteName = "\(appIdentifierPrefix)group.\(Bundle.main.bundleIdentifier!)"
return UserDefaults.init(suiteName: suiteName)!
@@ -53,15 +56,15 @@ struct AppDefaults {
static let addFolderAccountID = "addFolderAccountID"
}
static let isDeveloperBuild: Bool = {
let isDeveloperBuild: Bool = {
if let dev = Bundle.main.object(forInfoDictionaryKey: "DeveloperEntitlements") as? String, dev == "-dev" {
return true
}
return false
}()
static let isFirstRun: Bool = {
if let _ = AppDefaults.shared.object(forKey: Key.firstRunDate) as? Date {
let isFirstRun: Bool = {
if let _ = AppDefaults.store.object(forKey: Key.firstRunDate) as? Date {
return false
}
firstRunDate = Date()
@@ -80,34 +83,34 @@ struct AppDefaults {
}
}
static var addWebFeedAccountID: String? {
var addWebFeedAccountID: String? {
get {
return string(for: Key.addWebFeedAccountID)
return AppDefaults.string(for: Key.addWebFeedAccountID)
}
set {
setString(for: Key.addWebFeedAccountID, newValue)
AppDefaults.setString(for: Key.addWebFeedAccountID, newValue)
}
}
static var addWebFeedFolderName: String? {
var addWebFeedFolderName: String? {
get {
return string(for: Key.addWebFeedFolderName)
return AppDefaults.string(for: Key.addWebFeedFolderName)
}
set {
setString(for: Key.addWebFeedFolderName, newValue)
AppDefaults.setString(for: Key.addWebFeedFolderName, newValue)
}
}
static var addFolderAccountID: String? {
var addFolderAccountID: String? {
get {
return string(for: Key.addFolderAccountID)
return AppDefaults.string(for: Key.addFolderAccountID)
}
set {
setString(for: Key.addFolderAccountID, newValue)
AppDefaults.setString(for: Key.addFolderAccountID, newValue)
}
}
static var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
var activeExtensionPointIDs: [[AnyHashable : AnyHashable]]? {
get {
return UserDefaults.standard.object(forKey: Key.activeExtensionPointIDs) as? [[AnyHashable : AnyHashable]]
}
@@ -116,94 +119,94 @@ struct AppDefaults {
}
}
static var lastImageCacheFlushDate: Date? {
var lastImageCacheFlushDate: Date? {
get {
return date(for: Key.lastImageCacheFlushDate)
return AppDefaults.date(for: Key.lastImageCacheFlushDate)
}
set {
setDate(for: Key.lastImageCacheFlushDate, newValue)
AppDefaults.setDate(for: Key.lastImageCacheFlushDate, newValue)
}
}
static var timelineGroupByFeed: Bool {
var timelineGroupByFeed: Bool {
get {
return bool(for: Key.timelineGroupByFeed)
return AppDefaults.bool(for: Key.timelineGroupByFeed)
}
set {
setBool(for: Key.timelineGroupByFeed, newValue)
AppDefaults.setBool(for: Key.timelineGroupByFeed, newValue)
}
}
static var refreshClearsReadArticles: Bool {
var refreshClearsReadArticles: Bool {
get {
return bool(for: Key.refreshClearsReadArticles)
return AppDefaults.bool(for: Key.refreshClearsReadArticles)
}
set {
setBool(for: Key.refreshClearsReadArticles, newValue)
AppDefaults.setBool(for: Key.refreshClearsReadArticles, newValue)
}
}
static var timelineSortDirection: ComparisonResult {
var timelineSortDirection: ComparisonResult {
get {
return sortDirection(for: Key.timelineSortDirection)
return AppDefaults.sortDirection(for: Key.timelineSortDirection)
}
set {
setSortDirection(for: Key.timelineSortDirection, newValue)
AppDefaults.setSortDirection(for: Key.timelineSortDirection, newValue)
}
}
static var articleFullscreenAvailable: Bool {
var articleFullscreenAvailable: Bool {
get {
return bool(for: Key.articleFullscreenAvailable)
return AppDefaults.bool(for: Key.articleFullscreenAvailable)
}
set {
setBool(for: Key.articleFullscreenAvailable, newValue)
AppDefaults.setBool(for: Key.articleFullscreenAvailable, newValue)
}
}
static var articleFullscreenEnabled: Bool {
var articleFullscreenEnabled: Bool {
get {
return bool(for: Key.articleFullscreenEnabled)
return AppDefaults.bool(for: Key.articleFullscreenEnabled)
}
set {
setBool(for: Key.articleFullscreenEnabled, newValue)
AppDefaults.setBool(for: Key.articleFullscreenEnabled, newValue)
}
}
static var confirmMarkAllAsRead: Bool {
var confirmMarkAllAsRead: Bool {
get {
return bool(for: Key.confirmMarkAllAsRead)
return AppDefaults.bool(for: Key.confirmMarkAllAsRead)
}
set {
setBool(for: Key.confirmMarkAllAsRead, newValue)
AppDefaults.setBool(for: Key.confirmMarkAllAsRead, newValue)
}
}
static var lastRefresh: Date? {
var lastRefresh: Date? {
get {
return date(for: Key.lastRefresh)
return AppDefaults.date(for: Key.lastRefresh)
}
set {
setDate(for: Key.lastRefresh, newValue)
AppDefaults.setDate(for: Key.lastRefresh, newValue)
}
}
static var timelineNumberOfLines: Int {
var timelineNumberOfLines: Int {
get {
return int(for: Key.timelineNumberOfLines)
return AppDefaults.int(for: Key.timelineNumberOfLines)
}
set {
setInt(for: Key.timelineNumberOfLines, newValue)
AppDefaults.setInt(for: Key.timelineNumberOfLines, newValue)
}
}
static var timelineIconSize: IconSize {
var timelineIconSize: IconSize {
get {
let rawValue = AppDefaults.shared.integer(forKey: Key.timelineIconSize)
let rawValue = AppDefaults.store.integer(forKey: Key.timelineIconSize)
return IconSize(rawValue: rawValue) ?? IconSize.medium
}
set {
AppDefaults.shared.set(newValue.rawValue, forKey: Key.timelineIconSize)
AppDefaults.store.set(newValue.rawValue, forKey: Key.timelineIconSize)
}
}
@@ -217,7 +220,7 @@ struct AppDefaults {
Key.articleFullscreenAvailable: false,
Key.articleFullscreenEnabled: false,
Key.confirmMarkAllAsRead: true]
AppDefaults.shared.register(defaults: defaults)
AppDefaults.store.register(defaults: defaults)
}
}
@@ -242,27 +245,27 @@ private extension AppDefaults {
}
static func bool(for key: String) -> Bool {
return AppDefaults.shared.bool(forKey: key)
return AppDefaults.store.bool(forKey: key)
}
static func setBool(for key: String, _ flag: Bool) {
AppDefaults.shared.set(flag, forKey: key)
AppDefaults.store.set(flag, forKey: key)
}
static func int(for key: String) -> Int {
return AppDefaults.shared.integer(forKey: key)
return AppDefaults.store.integer(forKey: key)
}
static func setInt(for key: String, _ x: Int) {
AppDefaults.shared.set(x, forKey: key)
AppDefaults.store.set(x, forKey: key)
}
static func date(for key: String) -> Date? {
return AppDefaults.shared.object(forKey: key) as? Date
return AppDefaults.store.object(forKey: key) as? Date
}
static func setDate(for key: String, _ date: Date?) {
AppDefaults.shared.set(date, forKey: key)
AppDefaults.store.set(date, forKey: key)
}
static func sortDirection(for key:String) -> ComparisonResult {

View File

@@ -73,7 +73,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppDefaults.registerDefaults()
let isFirstRun = AppDefaults.isFirstRun
let isFirstRun = AppDefaults.shared.isFirstRun
if isFirstRun {
os_log("Is first run.", log: log, type: .info)
}
@@ -139,7 +139,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}
@objc func accountRefreshDidFinish(_ note: Notification) {
AppDefaults.lastRefresh = Date()
AppDefaults.shared.lastRefresh = Date()
}
// MARK: - API
@@ -170,7 +170,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
extensionFeedAddRequestFile.resume()
syncTimer?.update()
if let lastRefresh = AppDefaults.lastRefresh {
if let lastRefresh = AppDefaults.shared.lastRefresh {
if Date() > lastRefresh.addingTimeInterval(15 * 60) {
AccountManager.shared.refreshAll(errorHandler: ErrorHandler.log)
} else {

View File

@@ -132,7 +132,7 @@ class ArticleViewController: UIViewController {
articleExtractorButton.buttonState = controller.articleExtractorButtonState
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
if AppDefaults.articleFullscreenEnabled {
if AppDefaults.shared.articleFullscreenEnabled {
controller.hideBars()
}
@@ -183,7 +183,7 @@ class ArticleViewController: UIViewController {
starBarButtonItem.isEnabled = true
let permalinkPresent = article.preferredLink != nil
articleExtractorButton.isEnabled = permalinkPresent && !AppDefaults.isDeveloperBuild
articleExtractorButton.isEnabled = permalinkPresent && !AppDefaults.shared.isDeveloperBuild
actionBarButtonItem.isEnabled = permalinkPresent
if article.status.read {
@@ -230,7 +230,7 @@ class ArticleViewController: UIViewController {
@objc func willEnterForeground(_ note: Notification) {
// The toolbar will come back on you if you don't hide it again
if AppDefaults.articleFullscreenEnabled {
if AppDefaults.shared.articleFullscreenEnabled {
currentWebViewController?.hideBars()
}
}

View File

@@ -37,7 +37,7 @@ class WebViewController: UIViewController {
private lazy var contextMenuInteraction = UIContextMenuInteraction(delegate: self)
private var isFullScreenAvailable: Bool {
return AppDefaults.articleFullscreenAvailable && traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
return AppDefaults.shared.articleFullscreenAvailable && traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
}
private lazy var transition = ImageTransition(controller: self)
private var clickedImageCompletion: (() -> Void)?
@@ -155,7 +155,7 @@ class WebViewController: UIViewController {
}
func showBars() {
AppDefaults.articleFullscreenEnabled = false
AppDefaults.shared.articleFullscreenEnabled = false
coordinator.showStatusBar()
topShowBarsViewConstraint?.constant = 0
bottomShowBarsViewConstraint?.constant = 0
@@ -166,7 +166,7 @@ class WebViewController: UIViewController {
func hideBars() {
if isFullScreenAvailable {
AppDefaults.articleFullscreenEnabled = true
AppDefaults.shared.articleFullscreenEnabled = true
coordinator.hideStatusBar()
topShowBarsViewConstraint?.constant = -44.0
bottomShowBarsViewConstraint?.constant = 44.0
@@ -613,7 +613,7 @@ private extension WebViewController {
topShowBarsView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(topShowBarsView)
if AppDefaults.articleFullscreenEnabled {
if AppDefaults.shared.articleFullscreenEnabled {
topShowBarsViewConstraint = view.topAnchor.constraint(equalTo: topShowBarsView.bottomAnchor, constant: -44.0)
} else {
topShowBarsViewConstraint = view.topAnchor.constraint(equalTo: topShowBarsView.bottomAnchor, constant: 0.0)
@@ -633,7 +633,7 @@ private extension WebViewController {
topShowBarsView.backgroundColor = .clear
bottomShowBarsView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bottomShowBarsView)
if AppDefaults.articleFullscreenEnabled {
if AppDefaults.shared.articleFullscreenEnabled {
bottomShowBarsViewConstraint = view.bottomAnchor.constraint(equalTo: bottomShowBarsView.topAnchor, constant: 44.0)
} else {
bottomShowBarsViewConstraint = view.bottomAnchor.constraint(equalTo: bottomShowBarsView.topAnchor, constant: 0.0)

View File

@@ -29,7 +29,7 @@ struct MarkAsReadAlertController {
return
}
if AppDefaults.confirmMarkAllAsRead {
if AppDefaults.shared.confirmMarkAllAsRead {
let alertController = MarkAsReadAlertController.alert(coordinator: coordinator, confirmTitle: confirmTitle, cancelCompletion: cancelCompletion, sourceType: sourceType) { _ in
completion()
}

View File

@@ -68,8 +68,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
// Configure the table
tableView.dataSource = dataSource
numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize
numberOfTextLines = AppDefaults.shared.timelineNumberOfLines
iconSize = AppDefaults.shared.timelineIconSize
resetEstimatedRowHeight()
if let titleView = Bundle.main.loadNibNamed("MasterTimelineTitleView", owner: self, options: nil)?[0] as? MasterTimelineTitleView {
@@ -455,9 +455,9 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
}
@objc func userDefaultsDidChange(_ note: Notification) {
if numberOfTextLines != AppDefaults.timelineNumberOfLines || iconSize != AppDefaults.timelineIconSize {
numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize
if numberOfTextLines != AppDefaults.shared.timelineNumberOfLines || iconSize != AppDefaults.shared.timelineIconSize {
numberOfTextLines = AppDefaults.shared.timelineNumberOfLines
iconSize = AppDefaults.shared.timelineIconSize
resetEstimatedRowHeight()
reloadAllVisibleCells()
}

View File

@@ -87,7 +87,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
var isTimelineViewControllerPending = false
var isArticleViewControllerPending = false
private(set) var sortDirection = AppDefaults.timelineSortDirection {
private(set) var sortDirection = AppDefaults.shared.timelineSortDirection {
didSet {
if sortDirection != oldValue {
sortParametersDidChange()
@@ -95,7 +95,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
}
private(set) var groupByFeed = AppDefaults.timelineGroupByFeed {
private(set) var groupByFeed = AppDefaults.shared.timelineGroupByFeed {
didSet {
if groupByFeed != oldValue {
sortParametersDidChange()
@@ -546,8 +546,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
@objc func userDefaultsDidChange(_ note: Notification) {
self.sortDirection = AppDefaults.timelineSortDirection
self.groupByFeed = AppDefaults.timelineGroupByFeed
self.sortDirection = AppDefaults.shared.timelineSortDirection
self.groupByFeed = AppDefaults.shared.timelineGroupByFeed
}
@objc func accountDidDownloadArticles(_ note: Notification) {
@@ -582,7 +582,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
if isReadFeedsFiltered {
rebuildBackingStores()
}
if isReadArticlesFiltered && (AppDefaults.refreshClearsReadArticles || !conditional) {
if isReadArticlesFiltered && (AppDefaults.shared.refreshClearsReadArticles || !conditional) {
refreshTimeline(resetScroll: false)
}
}

View File

@@ -152,7 +152,7 @@ private extension AddAccountViewController {
}
}
if AppDefaults.isDeveloperBuild {
if AppDefaults.shared.isDeveloperBuild {
removeAccountType(.cloudKit)
removeAccountType(.feedly)
removeAccountType(.feedWrangler)

View File

@@ -46,31 +46,31 @@ class SettingsViewController: UITableViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if AppDefaults.timelineSortDirection == .orderedAscending {
if AppDefaults.shared.timelineSortDirection == .orderedAscending {
timelineSortOrderSwitch.isOn = true
} else {
timelineSortOrderSwitch.isOn = false
}
if AppDefaults.timelineGroupByFeed {
if AppDefaults.shared.timelineGroupByFeed {
groupByFeedSwitch.isOn = true
} else {
groupByFeedSwitch.isOn = false
}
if AppDefaults.refreshClearsReadArticles {
if AppDefaults.shared.refreshClearsReadArticles {
refreshClearsReadArticlesSwitch.isOn = true
} else {
refreshClearsReadArticlesSwitch.isOn = false
}
if AppDefaults.confirmMarkAllAsRead {
if AppDefaults.shared.confirmMarkAllAsRead {
confirmMarkAllAsReadSwitch.isOn = true
} else {
confirmMarkAllAsReadSwitch.isOn = false
}
if AppDefaults.articleFullscreenAvailable {
if AppDefaults.shared.articleFullscreenAvailable {
showFullscreenArticlesSwitch.isOn = true
} else {
showFullscreenArticlesSwitch.isOn = false
@@ -286,41 +286,41 @@ class SettingsViewController: UITableViewController {
@IBAction func switchTimelineOrder(_ sender: Any) {
if timelineSortOrderSwitch.isOn {
AppDefaults.timelineSortDirection = .orderedAscending
AppDefaults.shared.timelineSortDirection = .orderedAscending
} else {
AppDefaults.timelineSortDirection = .orderedDescending
AppDefaults.shared.timelineSortDirection = .orderedDescending
}
}
@IBAction func switchGroupByFeed(_ sender: Any) {
if groupByFeedSwitch.isOn {
AppDefaults.timelineGroupByFeed = true
AppDefaults.shared.timelineGroupByFeed = true
} else {
AppDefaults.timelineGroupByFeed = false
AppDefaults.shared.timelineGroupByFeed = false
}
}
@IBAction func switchClearsReadArticles(_ sender: Any) {
if refreshClearsReadArticlesSwitch.isOn {
AppDefaults.refreshClearsReadArticles = true
AppDefaults.shared.refreshClearsReadArticles = true
} else {
AppDefaults.refreshClearsReadArticles = false
AppDefaults.shared.refreshClearsReadArticles = false
}
}
@IBAction func switchConfirmMarkAllAsRead(_ sender: Any) {
if confirmMarkAllAsReadSwitch.isOn {
AppDefaults.confirmMarkAllAsRead = true
AppDefaults.shared.confirmMarkAllAsRead = true
} else {
AppDefaults.confirmMarkAllAsRead = false
AppDefaults.shared.confirmMarkAllAsRead = false
}
}
@IBAction func switchFullscreenArticles(_ sender: Any) {
if showFullscreenArticlesSwitch.isOn {
AppDefaults.articleFullscreenAvailable = true
AppDefaults.shared.articleFullscreenAvailable = true
} else {
AppDefaults.articleFullscreenAvailable = false
AppDefaults.shared.articleFullscreenAvailable = false
}
}

View File

@@ -27,11 +27,11 @@ class TimelineCustomizerViewController: UIViewController {
super.viewDidLoad()
iconSizeSliderContainerView.layer.cornerRadius = 10
iconSizeSlider.value = Float(AppDefaults.timelineIconSize.rawValue)
iconSizeSlider.value = Float(AppDefaults.shared.timelineIconSize.rawValue)
iconSizeSlider.addTickMarks()
numberOfLinesSliderContainerView.layer.cornerRadius = 10
numberOfLinesSlider.value = Float(AppDefaults.timelineNumberOfLines)
numberOfLinesSlider.value = Float(AppDefaults.shared.timelineNumberOfLines)
numberOfLinesSlider.addTickMarks()
}
@@ -48,12 +48,12 @@ class TimelineCustomizerViewController: UIViewController {
@IBAction func iconSizeChanged(_ sender: Any) {
guard let iconSize = IconSize(rawValue: Int(iconSizeSlider.value.rounded())) else { return }
AppDefaults.timelineIconSize = iconSize
AppDefaults.shared.timelineIconSize = iconSize
updatePreview()
}
@IBAction func numberOfLinesChanged(_ sender: Any) {
AppDefaults.timelineNumberOfLines = Int(numberOfLinesSlider.value.rounded())
AppDefaults.shared.timelineNumberOfLines = Int(numberOfLinesSlider.value.rounded())
updatePreview()
}

View File

@@ -71,7 +71,7 @@ private extension TimelinePreviewTableViewController {
let iconImage = IconImage(AppAssets.faviconTemplateImage.withTintColor(AppAssets.secondaryAccentColor))
return MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Feed Name", byline: nil, iconImage: iconImage, showIcon: true, featuredImage: nil, numberOfLines: AppDefaults.timelineNumberOfLines, iconSize: AppDefaults.timelineIconSize)
return MasterTimelineCellData(article: prototypeArticle, showFeedName: .feed, feedName: "Feed Name", byline: nil, iconImage: iconImage, showIcon: true, featuredImage: nil, numberOfLines: AppDefaults.shared.timelineNumberOfLines, iconSize: AppDefaults.shared.timelineIconSize)
}
}

View File

@@ -12,8 +12,8 @@ struct ShareDefaultContainer {
static func defaultContainer(containers: ExtensionContainers) -> ExtensionContainer? {
if let accountID = AppDefaults.addWebFeedAccountID, let account = containers.accounts.first(where: { $0.accountID == accountID }) {
if let folderName = AppDefaults.addWebFeedFolderName, let folder = account.folders.first(where: { $0.name == folderName }) {
if let accountID = AppDefaults.shared.addWebFeedAccountID, let account = containers.accounts.first(where: { $0.accountID == accountID }) {
if let folderName = AppDefaults.shared.addWebFeedFolderName, let folder = account.folders.first(where: { $0.name == folderName }) {
return folder
} else {
return substituteContainerIfNeeded(account: account)
@@ -27,11 +27,11 @@ struct ShareDefaultContainer {
}
static func saveDefaultContainer(_ container: ExtensionContainer) {
AppDefaults.addWebFeedAccountID = container.accountID
AppDefaults.shared.addWebFeedAccountID = container.accountID
if let folder = container as? ExtensionFolder {
AppDefaults.addWebFeedFolderName = folder.name
AppDefaults.shared.addWebFeedFolderName = folder.name
} else {
AppDefaults.addWebFeedFolderName = nil
AppDefaults.shared.addWebFeedFolderName = nil
}
}