Add settings scene.

This commit is contained in:
Maurice Parker 2019-04-25 18:06:53 -05:00
parent f5941fda64
commit 81f7332f3c
15 changed files with 631 additions and 84 deletions

View File

@ -107,6 +107,13 @@
51C452B82265178500C03939 /* styleSheet.css in Resources */ = {isa = PBXBuildFile; fileRef = 51C452B72265178500C03939 /* styleSheet.css */; };
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
51EC114C2149FE3300B296E3 /* FolderTreeMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */; };
51F85BE5227217D000C787DC /* RefreshIntervalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F85BDB2272162F00C787DC /* RefreshIntervalViewController.swift */; };
51F85BE7227245FC00C787DC /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F85BE6227245FC00C787DC /* AboutViewController.swift */; };
51F85BEB22724CB600C787DC /* About.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 51F85BEA22724CB600C787DC /* About.rtf */; };
51F85BED227251DF00C787DC /* Acknowledgments.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 51F85BEC227251DF00C787DC /* Acknowledgments.rtf */; };
51F85BEF2272520B00C787DC /* Thanks.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 51F85BEE2272520B00C787DC /* Thanks.rtf */; };
51F85BF12272524100C787DC /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 51F85BF02272524100C787DC /* Credits.rtf */; };
51F85BF32272531500C787DC /* Dedication.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 51F85BF22272531500C787DC /* Dedication.rtf */; };
6581C73820CED60100F4AD34 /* SafariExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */; };
6581C73A20CED60100F4AD34 /* SafariExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */; };
6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; };
@ -652,6 +659,13 @@
51C452B32265141B00C03939 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
51C452B72265178500C03939 /* styleSheet.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = styleSheet.css; sourceTree = "<group>"; };
51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FolderTreeMenu.swift; path = AddFeed/FolderTreeMenu.swift; sourceTree = "<group>"; };
51F85BDB2272162F00C787DC /* RefreshIntervalViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshIntervalViewController.swift; sourceTree = "<group>"; };
51F85BE6227245FC00C787DC /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
51F85BEA22724CB600C787DC /* About.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = About.rtf; sourceTree = "<group>"; };
51F85BEC227251DF00C787DC /* Acknowledgments.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Acknowledgments.rtf; sourceTree = "<group>"; };
51F85BEE2272520B00C787DC /* Thanks.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Thanks.rtf; sourceTree = "<group>"; };
51F85BF02272524100C787DC /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
51F85BF22272531500C787DC /* Dedication.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; path = Dedication.rtf; sourceTree = "<group>"; };
6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
6581C73420CED60100F4AD34 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionHandler.swift; sourceTree = "<group>"; };
@ -943,6 +957,8 @@
children = (
5183CCEC22711DCE0010922C /* Settings.storyboard */,
5183CCEE227125970010922C /* SettingsViewController.swift */,
51F85BDB2272162F00C787DC /* RefreshIntervalViewController.swift */,
51F85BE6227245FC00C787DC /* AboutViewController.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -1578,6 +1594,11 @@
84C9FC9A2262A1A900D921D6 /* Resources */ = {
isa = PBXGroup;
children = (
51F85BEA22724CB600C787DC /* About.rtf */,
51F85BF02272524100C787DC /* Credits.rtf */,
51F85BEC227251DF00C787DC /* Acknowledgments.rtf */,
51F85BEE2272520B00C787DC /* Thanks.rtf */,
51F85BF22272531500C787DC /* Dedication.rtf */,
51C452B72265178500C03939 /* styleSheet.css */,
84C9FC9B2262A1A900D921D6 /* Assets.xcassets */,
84C9FC9C2262A1A900D921D6 /* Info.plist */,
@ -2084,10 +2105,15 @@
files = (
51C452862265093600C03939 /* Add.storyboard in Resources */,
84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */,
51F85BF32272531500C787DC /* Dedication.rtf in Resources */,
84C9FCA42262A1B800D921D6 /* LaunchScreen.storyboard in Resources */,
51F85BEB22724CB600C787DC /* About.rtf in Resources */,
5183CCED22711DCE0010922C /* Settings.storyboard in Resources */,
51F85BED227251DF00C787DC /* Acknowledgments.rtf in Resources */,
51C452AB22650DC600C03939 /* template.html in Resources */,
51F85BF12272524100C787DC /* Credits.rtf in Resources */,
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
51F85BEF2272520B00C787DC /* Thanks.rtf in Resources */,
84C9FC9D2262A1A900D921D6 /* Assets.xcassets in Resources */,
51C452B82265178500C03939 /* styleSheet.css in Resources */,
);
@ -2210,11 +2236,13 @@
5126EE97226CB48A00C22AFC /* NavigationStateController.swift in Sources */,
51C4525A226508D600C03939 /* UIStoryboard-Extensions.swift in Sources */,
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */,
51F85BE5227217D000C787DC /* RefreshIntervalViewController.swift in Sources */,
51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */,
5183CCDF226F1FCC0010922C /* UINavigationController+Progress.swift in Sources */,
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */,
512E09352268B25900BDCFDD /* UISplitViewController-Extensions.swift in Sources */,
51C452A022650A1900C03939 /* FeedIconDownloader.swift in Sources */,
51F85BE7227245FC00C787DC /* AboutViewController.swift in Sources */,
51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */,
51C45292226509C800C03939 /* TodayFeedDelegate.swift in Sources */,
84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */,

View File

@ -35,4 +35,24 @@ enum RefreshInterval: Int {
return 8 * 60 * 60
}
}
func description() -> String {
switch self {
case .manually:
return NSLocalizedString("Manually", comment: "Manually")
case .every10Minutes:
return NSLocalizedString("Every 10 Minutes", comment: "Every 10 Minutes")
case .every30Minutes:
return NSLocalizedString("Every 30 Minutes", comment: "Every 30 Minutes")
case .everyHour:
return NSLocalizedString("Every Hour", comment: "Every Hour")
case .every2Hours:
return NSLocalizedString("Every 2 Hours", comment: "Every 2 Hours")
case .every4Hours:
return NSLocalizedString("Every 4 Hours", comment: "Every 4 Hours")
case .every8Hours:
return NSLocalizedString("Every 8 Hours", comment: "Every 8 Hours")
}
}
}

View File

@ -31,6 +31,9 @@ class AddContainerViewController: UIViewController {
private var currentViewController: AddContainerViewControllerChild?
var initialFeed: String?
var initialFeedName: String?
override func viewDidLoad() {
super.viewDidLoad()
@ -91,21 +94,32 @@ extension AddContainerViewController: AddContainerViewControllerChildDelegate {
private extension AddContainerViewController {
func switchToFeed() {
guard !(currentViewController is AddFeedViewController) else {
return
}
resetUI()
hideCurrentController()
displayContentController(UIStoryboard.add.instantiateController(ofType: AddFeedViewController.self))
let addFeedController = UIStoryboard.add.instantiateController(ofType: AddFeedViewController.self)
addFeedController.initialFeed = initialFeed
addFeedController.initialFeedName = initialFeedName
displayContentController(addFeedController)
}
func switchToFolder() {
guard !(currentViewController is AddFolderViewController) else {
return
}
resetUI()
hideCurrentController()
displayContentController(UIStoryboard.add.instantiateController(ofType: AddFolderViewController.self))
}
func resetUI() {

View File

@ -32,14 +32,23 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
private var userCancelled = false
var delegate: AddContainerViewControllerChildDelegate?
weak var delegate: AddContainerViewControllerChildDelegate?
var initialFeed: String?
var initialFeedName: String?
override func viewDidLoad() {
super.viewDidLoad()
urlTextField.autocorrectionType = .no
urlTextField.autocapitalizationType = .none
urlTextField.text = initialFeed
if initialFeed != nil {
delegate?.readyToAdd(state: true)
}
nameTextField.text = initialFeedName
pickerData = AddFeedFolderPickerData()
folderPickerView.dataSource = self

View File

@ -18,7 +18,7 @@ class AddFolderViewController: UITableViewController, AddContainerViewController
private var accounts: [Account]!
var delegate: AddContainerViewControllerChildDelegate?
weak var delegate: AddContainerViewControllerChildDelegate?
override func viewDidLoad() {

View File

@ -353,55 +353,13 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn
@IBAction func settings(_ sender: UIBarButtonItem) {
let settingsViewController = UIStoryboard.settings.instantiateInitialViewController()!
settingsViewController.modalPresentationStyle = .formSheet
self.present(settingsViewController, animated: true)
let settingsNavViewController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
settingsNavViewController.modalPresentationStyle = .formSheet
// let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
//
// // Settings Button
// let settingsTitle = NSLocalizedString("Settings", comment: "Settings")
// let setting = UIAlertAction(title: settingsTitle, style: .default) { alertAction in
//
// }
// optionMenu.addAction(setting)
//
// // Import Button
// let importOPMLTitle = NSLocalizedString("Import OPML", comment: "Import OPML")
// let importOPML = UIAlertAction(title: importOPMLTitle, style: .default) { [unowned self] alertAction in
// let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import)
// docPicker.delegate = self
// docPicker.modalPresentationStyle = .formSheet
// self.present(docPicker, animated: true)
// }
// optionMenu.addAction(importOPML)
//
// // Export Button
// let exportOPMLTitle = NSLocalizedString("Export OPML", comment: "Export OPML")
// let exportOPML = UIAlertAction(title: exportOPMLTitle, style: .default) { [unowned self] alertAction in
//
// let filename = "MySubscriptions.opml"
// let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
// let opmlString = OPMLExporter.OPMLString(with: AccountManager.shared.localAccount, title: filename)
// do {
// try opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
// } catch {
// self.presentError(title: "OPML Export Error", message: error.localizedDescription)
// }
//
// let docPicker = UIDocumentPickerViewController(url: tempFile, in: .exportToService)
// docPicker.modalPresentationStyle = .formSheet
// self.present(docPicker, animated: true)
//
// }
// optionMenu.addAction(exportOPML)
// optionMenu.addAction(UIAlertAction(title: "Cancel", style: .cancel))
//
// if let popoverController = optionMenu.popoverPresentationController {
// popoverController.barButtonItem = sender
// }
//
// self.present(optionMenu, animated: true)
let settingsViewController = settingsNavViewController.topViewController as! SettingsViewController
settingsViewController.presentingParentController = self
self.present(settingsNavViewController, animated: true)
}
@ -583,24 +541,6 @@ class MasterFeedViewController: ProgressTableViewController, UndoableCommandRunn
}
// MARK: OPML Document Picker
extension MasterFeedViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
for url in urls {
do {
try OPMLImporter.parseAndImport(fileURL: url, account: AccountManager.shared.localAccount)
} catch {
presentError(title: "OPML Import Error", message: error.localizedDescription)
}
}
}
}
// MARK: MasterTableViewCellDelegate
extension MasterFeedViewController: MasterFeedTableViewCellDelegate {

11
iOS/Resources/About.rtf Normal file
View File

@ -0,0 +1,11 @@
{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf400
{\fonttbl\f0\fnil\fcharset0 LucidaGrande-Bold;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\sl360\slmult1\pardirnatural\partightenfactor0
\f0\b\fs28 \cf2 By Brent Simmons
\fs22 \
{\field{\*\fldinst{HYPERLINK "http://inessential.com/"}}{\fldrslt
\fs28 inessential.com}}}

View File

@ -0,0 +1,10 @@
{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf400
{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\li360\sa60\partightenfactor0
{\field{\*\fldinst{HYPERLINK "https://github.com/ccgus/fmdb"}}{\fldrslt
\f0\fs28 \cf2 FMDB}}
\f0\fs28 \cf2 (greatest SQLite wrapper ever in history) is by {\field{\*\fldinst{HYPERLINK "http://flyingmeat.com/"}}{\fldrslt \cf2 Flying Meat Software}}.}

10
iOS/Resources/Credits.rtf Normal file
View File

@ -0,0 +1,10 @@
{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf400
{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\li360\sa60\partightenfactor0
\f0\fs28 \cf2 App icon and most other icons: {\field{\*\fldinst{HYPERLINK "https://twitter.com/BradEllis"}}{\fldrslt Brad Ellis}}\
Major code contributors: {\field{\*\fldinst{HYPERLINK "https://github.com/olofhellman"}}{\fldrslt Olof Hellman}}, {\field{\*\fldinst{HYPERLINK "https://github.com/vincode-io"}}{\fldrslt Maurice Parker}}, and {\field{\*\fldinst{HYPERLINK "https://github.com/danielpunkass"}}{\fldrslt Daniel Jalkut}}}

View File

@ -0,0 +1,9 @@
{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf400
{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\li360\sa60\partightenfactor0
\f0\fs28 \cf2 NetNewsWire 5.0 is dedicated to Aaron Swartz, Derek Miller, and Alex King, who helped with earlier versions of NetNewsWire, who I miss.}

9
iOS/Resources/Thanks.rtf Normal file
View File

@ -0,0 +1,9 @@
{\rtf1\ansi\ansicpg1252\cocoartf1671\cocoasubrtf400
{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\li360\sa60\partightenfactor0
\f0\fs28 \cf2 Thanks to Sheila and my family; thanks to my friends in Seattle and around the globe; thanks to my co-workers and friends at {\field{\*\fldinst{HYPERLINK "https://www.omnigroup.com/"}}{\fldrslt \cf2 The Omni Group}}; thanks to the ever-patient and ever-awesome NetNewsWire beta testers.}

View File

@ -0,0 +1,45 @@
//
// AboutViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 4/25/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
class AboutViewController: UITableViewController {
@IBOutlet weak var aboutTextView: UITextView!
@IBOutlet weak var creditsTextView: UITextView!
@IBOutlet weak var acknowledgmentsTextView: UITextView!
@IBOutlet weak var thanksTextView: UITextView!
@IBOutlet weak var dedicationTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
configureCell(file: "About", textView: aboutTextView)
configureCell(file: "Credits", textView: creditsTextView)
configureCell(file: "Acknowledgments", textView: acknowledgmentsTextView)
configureCell(file: "Thanks", textView: thanksTextView)
configureCell(file: "Dedication", textView: dedicationTextView)
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
}
private extension AboutViewController {
func configureCell(file: String, textView: UITextView) {
let url = Bundle.main.url(forResource: file, withExtension: "rtf")!
let string = try! NSAttributedString(url: url, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil)
textView.attributedText = string
}
}

View File

@ -0,0 +1,108 @@
//
// RefreshIntervalViewController.swift
// NetNewsWire
//
// Created by Maurice Parker on 4/25/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
class RefreshIntervalViewController: UITableViewController {
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 7
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let userRefreshInterval = AppDefaults.refreshInterval
switch indexPath.row {
case 0:
cell.textLabel?.text = RefreshInterval.manually.description()
if userRefreshInterval == RefreshInterval.manually {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
case 1:
cell.textLabel?.text = RefreshInterval.every10Minutes.description()
if userRefreshInterval == RefreshInterval.every10Minutes {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
case 2:
cell.textLabel?.text = RefreshInterval.every30Minutes.description()
if userRefreshInterval == RefreshInterval.every30Minutes {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
case 3:
cell.textLabel?.text = RefreshInterval.everyHour.description()
if userRefreshInterval == RefreshInterval.everyHour {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
case 4:
cell.textLabel?.text = RefreshInterval.every2Hours.description()
if userRefreshInterval == RefreshInterval.every2Hours {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
case 5:
cell.textLabel?.text = RefreshInterval.every4Hours.description()
if userRefreshInterval == RefreshInterval.every4Hours {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
default:
cell.textLabel?.text = RefreshInterval.every8Hours.description()
if userRefreshInterval == RefreshInterval.every8Hours {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let refreshInterval: RefreshInterval
switch indexPath.row {
case 0:
refreshInterval = RefreshInterval.manually
case 1:
refreshInterval = RefreshInterval.every10Minutes
case 2:
refreshInterval = RefreshInterval.every30Minutes
case 3:
refreshInterval = RefreshInterval.everyHour
case 4:
refreshInterval = RefreshInterval.every2Hours
case 5:
refreshInterval = RefreshInterval.every4Hours
default:
refreshInterval = RefreshInterval.every8Hours
}
AppDefaults.refreshInterval = refreshInterval
self.navigationController?.popViewController(animated: true)
}
}

View File

@ -166,6 +166,9 @@
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Keq-Np-l9O">
<rect key="frame" x="341" y="6.5" width="51" height="31"/>
<connections>
<action selector="switchTimelineOrder:" destination="a0p-rk-skQ" eventType="valueChanged" id="ARp-jk-sAo"/>
</connections>
</switch>
</subviews>
<constraints>
@ -274,11 +277,224 @@
</connections>
</barButtonItem>
</navigationItem>
<connections>
<outlet property="refreshIntervalLabel" destination="EYM-CI-6Os" id="a09-JG-chE"/>
<outlet property="timelineSortOrderSwitch" destination="Keq-Np-l9O" id="Zm7-HG-r5h"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="GZq-IX-Qod" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="465" y="152"/>
</scene>
<!--Refresh Interval-->
<scene sceneID="5WY-bu-OPU">
<objects>
<tableViewController storyboardIdentifier="RefreshIntervalViewController" title="Refresh Interval" useStoryboardIdentifierAsRestorationIdentifier="YES" id="Vd0-lF-iff" customClass="RefreshIntervalViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="KyE-ob-CYm">
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="91W-kj-0Dw">
<rect key="frame" x="0.0" y="55.5" width="414" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="91W-kj-0Dw" id="AXy-Ti-xiS">
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="Vd0-lF-iff" id="ZDd-4x-0M5"/>
<outlet property="delegate" destination="Vd0-lF-iff" id="3tH-oh-oZ3"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Refresh Interval" id="lIq-gS-6ui"/>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="PkF-Up-3qC" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1310" y="151"/>
</scene>
<!--About-->
<scene sceneID="pWd-ql-XAA">
<objects>
<tableViewController storyboardIdentifier="AboutViewController" title="About" id="K5w-58-sQW" customClass="AboutViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="ybg-ki-AbJ">
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<sections>
<tableViewSection id="apW-l0-gWz">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="62" id="zbQ-3A-f3f">
<rect key="frame" x="0.0" y="35" width="414" height="62"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zbQ-3A-f3f" id="5Al-LU-dRg">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UgA-s6-Vvg">
<rect key="frame" x="20" y="11" width="180.5" height="40"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="29"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="UgA-s6-Vvg" firstAttribute="leading" secondItem="5Al-LU-dRg" secondAttribute="leadingMargin" id="Zta-l2-4EX"/>
<constraint firstAttribute="bottomMargin" secondItem="UgA-s6-Vvg" secondAttribute="bottom" id="aiW-uE-G1W"/>
<constraint firstItem="UgA-s6-Vvg" firstAttribute="top" secondItem="5Al-LU-dRg" secondAttribute="topMargin" id="t4t-Ln-miK"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="62" id="2DV-bO-vyT">
<rect key="frame" x="0.0" y="97" width="414" height="62"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2DV-bO-vyT" id="YUn-eb-xyx">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5fQ-qz-qbW">
<rect key="frame" x="16" y="0.0" width="382" height="61.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="5fQ-qz-qbW" secondAttribute="trailing" constant="16" id="4df-cD-jcj"/>
<constraint firstAttribute="bottom" secondItem="5fQ-qz-qbW" secondAttribute="bottom" id="6Ww-EY-vtA"/>
<constraint firstItem="5fQ-qz-qbW" firstAttribute="top" secondItem="YUn-eb-xyx" secondAttribute="top" id="FJR-f7-rzH"/>
<constraint firstItem="5fQ-qz-qbW" firstAttribute="leading" secondItem="YUn-eb-xyx" secondAttribute="leading" constant="16" id="xMM-EX-fhp"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="CREDITS" id="O1X-Iq-ibE">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="62" id="ZY4-id-Iia">
<rect key="frame" x="0.0" y="215" width="414" height="62"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="ZY4-id-Iia" id="IPw-QQ-LYI">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LiZ-Tv-tqb">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<constraints>
<constraint firstItem="LiZ-Tv-tqb" firstAttribute="leading" secondItem="IPw-QQ-LYI" secondAttribute="leading" id="W80-12-bLK"/>
<constraint firstAttribute="bottom" secondItem="LiZ-Tv-tqb" secondAttribute="bottom" id="Wvc-BI-NSb"/>
<constraint firstItem="LiZ-Tv-tqb" firstAttribute="top" secondItem="IPw-QQ-LYI" secondAttribute="top" id="ZNh-Nt-Oyx"/>
<constraint firstAttribute="trailing" secondItem="LiZ-Tv-tqb" secondAttribute="trailing" id="vak-Ou-mLe"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="ACKNOWLEDGMENTS" id="0Jq-ba-ylz">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="62" id="0Ge-wc-h3h">
<rect key="frame" x="0.0" y="333" width="414" height="62"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="0Ge-wc-h3h" id="tFb-3V-qIg">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YLf-rp-9nE">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="YLf-rp-9nE" secondAttribute="trailing" id="PdJ-fJ-4Kg"/>
<constraint firstItem="YLf-rp-9nE" firstAttribute="top" secondItem="tFb-3V-qIg" secondAttribute="top" id="XqY-0L-gjF"/>
<constraint firstItem="YLf-rp-9nE" firstAttribute="leading" secondItem="tFb-3V-qIg" secondAttribute="leading" id="wBJ-7b-fOB"/>
<constraint firstAttribute="bottom" secondItem="YLf-rp-9nE" secondAttribute="bottom" id="yLw-Qs-gyo"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="THANKS" id="Sgx-f1-hsT">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="62" id="bLz-mu-psL">
<rect key="frame" x="0.0" y="451" width="414" height="62"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="bLz-mu-psL" id="cxy-IZ-zFZ">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wTL-xl-1rK">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="wTL-xl-1rK" secondAttribute="bottom" id="5oR-mb-FFd"/>
<constraint firstItem="wTL-xl-1rK" firstAttribute="leading" secondItem="cxy-IZ-zFZ" secondAttribute="leading" id="HC7-dW-NNl"/>
<constraint firstAttribute="trailing" secondItem="wTL-xl-1rK" secondAttribute="trailing" id="NUc-ZH-lhc"/>
<constraint firstItem="wTL-xl-1rK" firstAttribute="top" secondItem="cxy-IZ-zFZ" secondAttribute="top" id="bSe-CP-PGs"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="DEDICATION" id="nbm-Bs-te6">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="62" id="aab-HD-ce6">
<rect key="frame" x="0.0" y="569" width="414" height="62"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="aab-HD-ce6" id="6pH-5O-3V8">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DIp-6a-oPH">
<rect key="frame" x="0.0" y="0.0" width="414" height="61.5"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<attributedString key="attributedText"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<constraints>
<constraint firstItem="DIp-6a-oPH" firstAttribute="top" secondItem="6pH-5O-3V8" secondAttribute="top" id="2Y3-QM-2cP"/>
<constraint firstAttribute="bottom" secondItem="DIp-6a-oPH" secondAttribute="bottom" id="48b-wD-ZEf"/>
<constraint firstAttribute="trailing" secondItem="DIp-6a-oPH" secondAttribute="trailing" id="DMB-m4-JCA"/>
<constraint firstItem="DIp-6a-oPH" firstAttribute="leading" secondItem="6pH-5O-3V8" secondAttribute="leading" id="tG6-27-iv0"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="K5w-58-sQW" id="bn5-UA-9O9"/>
<outlet property="delegate" destination="K5w-58-sQW" id="0rW-hs-5m8"/>
</connections>
</tableView>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
<connections>
<outlet property="aboutTextView" destination="5fQ-qz-qbW" id="R2a-em-Nq0"/>
<outlet property="acknowledgmentsTextView" destination="YLf-rp-9nE" id="vRI-bi-wef"/>
<outlet property="creditsTextView" destination="LiZ-Tv-tqb" id="x3n-Vb-KYr"/>
<outlet property="dedicationTextView" destination="DIp-6a-oPH" id="iCz-xX-xEn"/>
<outlet property="thanksTextView" destination="wTL-xl-1rK" id="6qr-Ue-2jp"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="kRt-nH-nOf" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2092.753623188406" y="150.66964285714286"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Ezn-Ny-zye">
<objects>

View File

@ -7,28 +7,146 @@
//
import UIKit
import Account
class SettingsViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
@IBOutlet weak var refreshIntervalLabel: UILabel!
@IBOutlet weak var timelineSortOrderSwitch: UISwitch!
weak var presentingParentController: UIViewController?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if AppDefaults.timelineSortDirection == .orderedAscending {
timelineSortOrderSwitch.isOn = true
} else {
timelineSortOrderSwitch.isOn = false
}
// Do any additional setup after loading the view.
}
refreshIntervalLabel.text = AppDefaults.refreshInterval.description()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0:
print("Accounts isn't ready yet")
case 1:
switch indexPath.row {
case 0:
let timeline = UIStoryboard.settings.instantiateController(ofType: AboutViewController.self)
self.navigationController?.pushViewController(timeline, animated: true)
case 1:
UIApplication.shared.open(URL(string: "https://ranchero.com/netnewswire/")!, options: [:])
case 2:
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire")!, options: [:])
case 3:
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/issues")!, options: [:])
default:
UIApplication.shared.open(URL(string: "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")!, options: [:])
}
case 2:
UIApplication.shared.open(URL(string: "https://appcamp4girls.com/contribute/")!, options: [:])
default:
switch indexPath.row {
case 0:
let timeline = UIStoryboard.settings.instantiateController(ofType: RefreshIntervalViewController.self)
self.navigationController?.pushViewController(timeline, animated: true)
case 1:
addFeed()
case 2:
importOPML()
case 3:
exportOPML()
default:
print("export")
}
}
}
@IBAction func done(_ sender: Any) {
dismiss(animated: true)
}
@IBAction func switchTimelineOrder(_ sender: Any) {
if timelineSortOrderSwitch.isOn {
AppDefaults.timelineSortDirection = .orderedAscending
} else {
AppDefaults.timelineSortDirection = .orderedDescending
}
}
}
// MARK: OPML Document Picker
extension SettingsViewController: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
for url in urls {
do {
try OPMLImporter.parseAndImport(fileURL: url, account: AccountManager.shared.localAccount)
} catch {
presentError(title: "OPML Import Error", message: error.localizedDescription)
}
}
}
}
// MARK: Private
private extension SettingsViewController {
func addFeed() {
let appNewsURLString = "https://nnw.ranchero.com/feed.json"
if AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) {
presentError(title: "Subscribe", message: "You are already subscribed to the NetNewsWire news feed.")
return
}
self.dismiss(animated: true)
let addNavViewController = UIStoryboard.add.instantiateInitialViewController() as! UINavigationController
let addViewController = addNavViewController.topViewController as! AddContainerViewController
addNavViewController.modalPresentationStyle = .formSheet
addViewController.initialFeed = appNewsURLString
addViewController.initialFeedName = "NetNewsWire News"
presentingParentController?.present(addNavViewController, animated: true)
}
func importOPML() {
let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import)
docPicker.delegate = self
docPicker.modalPresentationStyle = .formSheet
self.present(docPicker, animated: true)
}
func exportOPML() {
let filename = "MySubscriptions.opml"
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
let opmlString = OPMLExporter.OPMLString(with: AccountManager.shared.localAccount, title: filename)
do {
try opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
} catch {
self.presentError(title: "OPML Export Error", message: error.localizedDescription)
}
let docPicker = UIDocumentPickerViewController(url: tempFile, in: .exportToService)
docPicker.modalPresentationStyle = .formSheet
self.present(docPicker, animated: true)
}
}