Make importing NetNewsWire 3 subscriptions a one-step process — use an accessory view in the NSOpenPanel. Also: refactor to use immutable structs instead of classes; use static methods on structs; reduce API surface; don’t flatten sub-folders (let the OPML importer handle the flattening, which it already does).

This commit is contained in:
Brent Simmons 2019-10-17 13:25:11 -07:00
parent 19145505c5
commit 56fab24cec
14 changed files with 385 additions and 490 deletions

View File

@ -57,7 +57,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
private var addFeedController: AddFeedController?
private var addFolderWindowController: AddFolderWindowController?
private var importOPMLController: ImportOPMLWindowController?
private var importNNW3Controller: ImportNNW3WindowController?
private var exportOPMLController: ExportOPMLWindowController?
private var keyboardShortcutsWindowController: WebViewWindowController?
private var inspectorWindowController: InspectorWindowController?
@ -127,8 +126,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
logDebugMessage("Is first run.")
}
let localAccount = AccountManager.shared.defaultAccount
NNW3FeedsImporter.importIfNeeded(isFirstRun, account: localAccount)
DefaultFeedsImporter.importIfNeeded(isFirstRun, account: localAccount)
if isFirstRun && !AccountManager.shared.anyAccountHasAtLeastOneFeed() {
// Import feeds. Either old NNW 3 feeds or the default feeds.
if !NNW3ImportController.importSubscriptionsIfFileExists(account: localAccount) {
DefaultFeedsImporter.importDefaultFeeds(account: localAccount)
}
}
let tempDirectory = NSTemporaryDirectory()
let bundleIdentifier = (Bundle.main.infoDictionary!["CFBundleIdentifier"]! as! String)
@ -425,9 +429,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
if mainWindowController!.isDisplayingSheet {
return
}
importNNW3Controller = ImportNNW3WindowController()
importNNW3Controller?.runSheetOnWindow(mainWindowController!.window!)
NNW3ImportController.askUserToImportNNW3Subscriptions(window: mainWindowController!.window!)
}
@IBAction func exportOPML(_ sender: Any?) {

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15504"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15400"/>
</dependencies>
<scenes>
<!--Application-->
@ -92,7 +92,7 @@
<action selector="importOPMLFromFile:" target="Ady-hI-5gd" id="eGY-fm-uvK"/>
</connections>
</menuItem>
<menuItem title="Import NNW 3 Subscriptions ..." id="ely-yi-STg">
<menuItem title="Import NNW 3 Subscriptions" id="ely-yi-STg">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="importNNW3FromFile:" target="Voe-Tx-rLC" id="mu3-gN-7uA"/>

View File

@ -1,104 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15504"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="ImportNNW3WindowController" customModule="NetNewsWire" customModuleProvider="target">
<connections>
<outlet property="accountPopUpButton" destination="sEU-ot-DE2" id="9W3-o6-YsH"/>
<outlet property="window" destination="QvC-M9-y7g" id="KF3-y6-Xy7"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="433" height="183"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
<view key="contentView" wantsLayer="YES" misplaced="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="433" height="183"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="vE6-sv-BA0">
<rect key="frame" x="18" y="100" width="405" height="32"/>
<textFieldCell key="cell" selectable="YES" title="Choose the account to get the imported subscriptions. This requires a NetNewsWire 3 Subscriptions.plist file." id="1Vu-Te-PGl">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kZ4-y9-lYy">
<rect key="frame" x="18" y="64" width="63" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Account:" id="e9g-7H-VWa">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sEU-ot-DE2" userLabel="Account Popup">
<rect key="frame" x="85" y="58" width="339" height="25"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="xsd-12-2yb" id="NuO-Hk-nk3">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="8LY-np-ij1">
<items>
<menuItem title="Item 1" state="on" id="xsd-12-2yb"/>
<menuItem title="Item 2" id="JGa-5R-SV5"/>
<menuItem title="Item 3" id="92e-hX-kYj"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ceu-mM-EKm">
<rect key="frame" x="81" y="13" width="173" height="32"/>
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="9ab-cB-hex">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
Gw
</string>
</buttonCell>
<connections>
<action selector="cancel:" target="-2" id="TTV-OR-P3d"/>
</connections>
</button>
<button horizontalHuggingPriority="750" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="et6-I1-6wB">
<rect key="frame" x="254" y="13" width="173" height="32"/>
<buttonCell key="cell" type="push" title="Import from NNW 3 …" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="dhV-on-ayM">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
<connections>
<action selector="importNNW3:" target="-2" id="C33-8m-XQA"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="vE6-sv-BA0" secondAttribute="trailing" constant="20" symbolic="YES" id="2UM-dU-XgH"/>
<constraint firstItem="kZ4-y9-lYy" firstAttribute="top" secondItem="vE6-sv-BA0" secondAttribute="bottom" constant="20" id="8Wf-Iv-teA"/>
<constraint firstItem="sEU-ot-DE2" firstAttribute="firstBaseline" secondItem="kZ4-y9-lYy" secondAttribute="firstBaseline" id="EPd-vD-8pa"/>
<constraint firstItem="et6-I1-6wB" firstAttribute="width" secondItem="ceu-mM-EKm" secondAttribute="width" id="Ldw-C8-F5X"/>
<constraint firstItem="vE6-sv-BA0" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" symbolic="YES" id="M8o-2f-uax"/>
<constraint firstItem="et6-I1-6wB" firstAttribute="top" secondItem="sEU-ot-DE2" secondAttribute="bottom" constant="20" symbolic="YES" id="OXm-Ns-r9Z"/>
<constraint firstItem="kZ4-y9-lYy" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" symbolic="YES" id="SZo-1P-yYA"/>
<constraint firstAttribute="bottom" secondItem="et6-I1-6wB" secondAttribute="bottom" constant="20" symbolic="YES" id="T9O-XC-wdY"/>
<constraint firstItem="et6-I1-6wB" firstAttribute="leading" secondItem="ceu-mM-EKm" secondAttribute="trailing" constant="12" symbolic="YES" id="Y8m-P8-JQh"/>
<constraint firstItem="sEU-ot-DE2" firstAttribute="leading" secondItem="kZ4-y9-lYy" secondAttribute="trailing" constant="8" symbolic="YES" id="a0f-Ak-Zdq"/>
<constraint firstAttribute="trailing" secondItem="et6-I1-6wB" secondAttribute="trailing" constant="20" symbolic="YES" id="chg-8b-D51"/>
<constraint firstItem="ceu-mM-EKm" firstAttribute="centerY" secondItem="et6-I1-6wB" secondAttribute="centerY" id="eKt-vM-3wn"/>
<constraint firstItem="ceu-mM-EKm" firstAttribute="leading" secondItem="sEU-ot-DE2" secondAttribute="leading" id="gb5-bZ-0R6"/>
<constraint firstItem="vE6-sv-BA0" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" symbolic="YES" id="ggA-m1-5pj"/>
<constraint firstAttribute="trailing" secondItem="sEU-ot-DE2" secondAttribute="trailing" constant="20" symbolic="YES" id="sFO-00-DR0"/>
</constraints>
</view>
<point key="canvasLocation" x="99.5" y="103.5"/>
</window>
</objects>
</document>

View File

@ -1,110 +0,0 @@
//
// ImportNNW3WindowController.swift
// NetNewsWire
//
// Created by Maurice Parker on 10/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import AppKit
import Account
class ImportNNW3WindowController: NSWindowController {
@IBOutlet weak var accountPopUpButton: NSPopUpButton!
private weak var hostWindow: NSWindow?
convenience init() {
self.init(windowNibName: NSNib.Name("ImportNNW3Sheet"))
}
override func windowDidLoad() {
accountPopUpButton.removeAllItems()
let menu = NSMenu()
accountPopUpButton.menu = menu
for oneAccount in AccountManager.shared.sortedActiveAccounts {
let oneMenuItem = NSMenuItem()
oneMenuItem.title = oneAccount.nameForDisplay
oneMenuItem.representedObject = oneAccount
menu.addItem(oneMenuItem)
if oneAccount.accountID == AppDefaults.importOPMLAccountID {
accountPopUpButton.select(oneMenuItem)
}
}
}
// MARK: API
func runSheetOnWindow(_ hostWindow: NSWindow) {
self.hostWindow = hostWindow
if AccountManager.shared.activeAccounts.count == 1 {
let account = AccountManager.shared.activeAccounts.first!
importNNW3(account: account)
} else {
hostWindow.beginSheet(window!)
}
}
// MARK: Actions
@IBAction func cancel(_ sender: Any) {
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.cancel)
}
@IBAction func importNNW3(_ sender: Any) {
guard let menuItem = accountPopUpButton.selectedItem else {
return
}
let account = menuItem.representedObject as! Account
AppDefaults.importOPMLAccountID = account.accountID
hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK)
importNNW3(account: account)
}
func importNNW3(account: Account) {
let panel = NSOpenPanel()
panel.canDownloadUbiquitousContents = true
panel.canResolveUbiquitousConflicts = true
panel.canChooseFiles = true
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.resolvesAliases = true
panel.directoryURL = URL(fileURLWithPath: NNW3PlistConverter.defaultFilePath)
panel.allowedFileTypes = ["plist"]
panel.allowsOtherFileTypes = false
panel.beginSheetModal(for: hostWindow!) { modalResult in
if modalResult == NSApplication.ModalResponse.OK, let url = panel.url {
guard let opmlURL = NNW3PlistConverter.convertToOPML(url: url) else {
return
}
account.importOPML(opmlURL) { result in
try? FileManager.default.removeItem(at: opmlURL)
switch result {
case .success:
break
case .failure(let error):
NSApplication.shared.presentError(error)
}
}
}
}
}
}

View File

@ -7,47 +7,138 @@
//
import Foundation
import RSCore
class NNW3Document: NNW3Entry {
init(plist: [[String: Any]]) {
super.init(title: "NNW3")
for child in plist {
if child["isContainer"] as? Bool ?? false {
entries.append(NNW3Entry(plist: child, parent: self))
} else {
entries.append(NNW3Feed(plist: child, parent: self))
}
}
struct NNW3Document {
private let children: [OPMLRepresentable]?
private init(plist: [[String: AnyObject]]) {
self.children = NNW3Folder.itemsWithPlist(plist: plist)
}
override func makeXML(indentLevel: Int) -> String {
init?(subscriptionsPlistURL url: URL) {
guard let data = try? Data(contentsOf: url) else {
return nil
}
guard let plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [[String: AnyObject]] else {
return nil
}
self.init(plist: plist)
}
}
// MARK: OPMLRepresentable
extension NNW3Document: OPMLRepresentable {
func OPMLString(indentLevel: Int, strictConformance: Bool) -> String {
var s =
"""
<?xml version="1.0" encoding="UTF-8"?>
<opml version="1.1">
<head>
<title>\(title ?? "")</title>
<title>NetNewsWire 3 Subscriptions</title>
</head>
<body>
"""
for entry in entries {
s += entry.makeXML(indentLevel: indentLevel + 1)
if let children = children {
for child in children {
s += child.OPMLString(indentLevel: indentLevel + 1, strictConformance: true)
}
}
s +=
"""
</body>
</opml>
"""
</body>
</opml>
"""
return s
}
}
// MARK: - NNW3Folder
private struct NNW3Folder {
private let title: String?
private let children: [OPMLRepresentable]?
init(plist: [String: Any]) {
self.title = plist["name"] as? String
guard let childrenArray = plist["childrenArray"] as? [[String: Any]] else {
self.children = nil
return
}
self.children = NNW3Folder.itemsWithPlist(plist: childrenArray)
}
static func itemsWithPlist(plist: [[String: Any]]) -> [OPMLRepresentable]? {
// Also used by NNW3Document.
var items = [OPMLRepresentable]()
for child in plist {
if child["isContainer"] as? Bool ?? false {
items.append(NNW3Folder(plist: child))
} else {
items.append(NNW3Feed(plist: child))
}
}
return items.isEmpty ? nil : items
}
}
// MARK: OPMLRepresentable
extension NNW3Folder: OPMLRepresentable {
func OPMLString(indentLevel: Int, strictConformance: Bool) -> String {
let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
guard let children = children else {
// Empty folder.
return "<outline text=\"\(t)\" title=\"\(t)\" />\n".rs_string(byPrependingNumberOfTabs: indentLevel)
}
var s = "<outline text=\"\(t)\" title=\"\(t)\">\n".rs_string(byPrependingNumberOfTabs: indentLevel)
for child in children {
s += child.OPMLString(indentLevel: indentLevel + 1, strictConformance: true)
}
s += "</outline>\n".rs_string(byPrependingNumberOfTabs: indentLevel)
return s
}
}
// MARK: - NNW3Feed
private struct NNW3Feed {
private let title: String?
private let homePageURL: String?
private let feedURL: String?
init(plist: [String: Any]) {
self.title = plist["name"] as? String
self.homePageURL = plist["home"] as? String
self.feedURL = plist["rss"] as? String
}
}
// MARK: OPMLRepresentable
extension NNW3Feed: OPMLRepresentable {
func OPMLString(indentLevel: Int, strictConformance: Bool) -> String {
let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
let p = homePageURL?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
let f = feedURL?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
var s = "<outline text=\"\(t)\" title=\"\(t)\" description=\"\" type=\"rss\" version=\"RSS\" htmlUrl=\"\(p)\" xmlUrl=\"\(f)\"/>\n"
s = s.rs_string(byPrependingNumberOfTabs: indentLevel)
return s
}
}

View File

@ -1,61 +0,0 @@
//
// NNW3Entry.swift
// NetNewsWire
//
// Created by Maurice Parker on 10/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
import RSCore
class NNW3Entry {
var title: String?
var entries = [NNW3Entry]()
weak var parent: NNW3Entry?
var isFolder: Bool {
return type(of: self) == NNW3Entry.self
}
init(title: String?, parent: NNW3Entry? = nil) {
self.title = title
self.parent = parent
}
convenience init(plist: [String: Any], parent: NNW3Entry? = nil) {
let title = plist["name"] as? String
self.init(title: title, parent: parent)
guard let childrenArray = plist["childrenArray"] as? [[String: AnyObject]] else {
return
}
for child in childrenArray {
if child["isContainer"] as? Bool ?? false {
entries.append(NNW3Entry(plist: child, parent: self))
} else {
entries.append(NNW3Feed(plist: child, parent: self))
}
}
}
func makeXML(indentLevel: Int) -> String {
let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
var s = "<outline text=\"\(t)\" title=\"\(t)\">\n".rs_string(byPrependingNumberOfTabs: indentLevel)
for entry in entries {
s += entry.makeXML(indentLevel: indentLevel + 1)
}
s += "</outline>\n".rs_string(byPrependingNumberOfTabs: indentLevel)
return s
}
}

View File

@ -1,48 +0,0 @@
//
// NNW3Feed.swift
// NetNewsWire
//
// Created by Maurice Parker on 10/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
import RSCore
class NNW3Feed: NNW3Entry {
var pageURL: String?
var feedURL: String?
init(feedURL: String) {
super.init(title: nil)
self.feedURL = feedURL
}
init(title: String?, pageURL: String?, feedURL: String?, parent: NNW3Entry? = nil) {
super.init(title: title, parent: parent)
self.pageURL = pageURL
self.feedURL = feedURL
}
convenience init(plist: [String: Any], parent: NNW3Entry? = nil) {
let title = plist["name"] as? String
let pageURL = plist["home"] as? String
let feedURL = plist["rss"] as? String
self.init(title: title, pageURL: pageURL, feedURL: feedURL, parent: parent)
}
override func makeXML(indentLevel: Int) -> String {
let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
let p = pageURL?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
let f = feedURL?.rs_stringByEscapingSpecialXMLCharacters() ?? ""
var s = "<outline text=\"\(t)\" title=\"\(t)\" description=\"\" type=\"rss\" version=\"RSS\" htmlUrl=\"\(p)\" xmlUrl=\"\(f)\"/>\n"
s = s.rs_string(byPrependingNumberOfTabs: indentLevel)
return s
}
}

View File

@ -1,50 +0,0 @@
//
// NNW3FeedsImporter.swift
// NetNewsWire
//
// Created by Maurice Parker on 10/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
import Account
import RSCore
struct NNW3FeedsImporter {
static func importIfNeeded(_ isFirstRun: Bool, account: Account) {
guard shouldImportDefaultFeeds(isFirstRun) else {
return
}
if !FileManager.default.fileExists(atPath: NNW3PlistConverter.defaultFilePath) {
return
}
appDelegate.logDebugMessage("Importing NNW3 feeds.")
let url = URL(fileURLWithPath: NNW3PlistConverter.defaultFilePath)
guard let opmlURL = NNW3PlistConverter.convertToOPML(url: url) else {
return
}
account.importOPML(opmlURL) { result in
try? FileManager.default.removeItem(at: opmlURL)
switch result {
case .success:
appDelegate.logDebugMessage("Importing NNW3 feeds succeeded.")
case .failure(let error):
appDelegate.logDebugMessage("Importing NNW3 feeds failed. \(error.localizedDescription)")
}
}
}
private static func shouldImportDefaultFeeds(_ isFirstRun: Bool) -> Bool {
if !isFirstRun || AccountManager.shared.anyAccountHasAtLeastOneFeed() {
return false
}
return true
}
}

View File

@ -0,0 +1,108 @@
//
// NNW3ImportController.swift
// NetNewsWire
//
// Created by Brent Simmons on 10/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import AppKit
import Account
struct NNW3ImportController {
/// Import NNW3 subscriptions if they exist.
/// Return true if Subscriptions.plist was found and subscriptions were imported.
static func importSubscriptionsIfFileExists(account: Account) -> Bool {
guard let subscriptionsPlistURL = defaultFileURL else {
return false
}
if !FileManager.default.fileExists(atPath: subscriptionsPlistURL.path) {
return false
}
NNW3ImportController.importSubscriptionsPlist(subscriptionsPlistURL, into: account)
return true
}
/// Run an NSOpenPanel and import subscriptions (if the user chooses to).
static func askUserToImportNNW3Subscriptions(window: NSWindow) {
chooseFile(window)
}
}
private extension NNW3ImportController {
/// URL to ~/Library/Application Support/NetNewsWire/Subscriptions.plist
static var defaultFileURL: URL? {
guard let applicationSupportURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else {
return nil
}
let folderURL = applicationSupportURL.appendingPathComponent("NetNewsWire", isDirectory: true)
return folderURL.appendingPathComponent("Subscriptions.plist", isDirectory: false)
}
/// Import Subscriptions.plist file. Convert to OPML and then import into specified Account.
static func importSubscriptionsPlist(_ subscriptionsPlistURL: URL, into account: Account) {
guard let opmlURL = convertToOPMLFile(subscriptionsPlistURL: subscriptionsPlistURL) else {
return
}
account.importOPML(opmlURL) { result in
try? FileManager.default.removeItem(at: opmlURL)
switch result {
case .success:
break
case .failure(let error):
NSApplication.shared.presentError(error)
}
}
}
/// Run the NSOpenPanel. On success, import subscriptions to the selected account.
static func chooseFile(_ window: NSWindow) {
let accessoryViewController = NNW3OpenPanelAccessoryViewController()
let panel = NSOpenPanel()
panel.canDownloadUbiquitousContents = true
panel.canResolveUbiquitousConflicts = true
panel.canChooseFiles = true
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
panel.resolvesAliases = true
panel.directoryURL = NNW3ImportController.defaultFileURL
panel.allowedFileTypes = ["plist"]
panel.allowsOtherFileTypes = false
panel.accessoryView = accessoryViewController.view
panel.isAccessoryViewDisclosed = true
panel.title = NSLocalizedString("Choose a Subscriptions.plist file:", comment: "NNW3 Import")
panel.beginSheetModal(for: window) { modalResult in
guard modalResult == .OK, let subscriptionsPlistURL = panel.url else {
return
}
guard let account = accessoryViewController.selectedAccount else {
return
}
AppDefaults.importOPMLAccountID = account.accountID
NNW3ImportController.importSubscriptionsPlist(subscriptionsPlistURL, into: account)
}
}
/// Convert Subscriptions.plist on disk to a temporary OPML file.
static func convertToOPMLFile(subscriptionsPlistURL url: URL) -> URL? {
guard let document = NNW3Document(subscriptionsPlistURL: url) else {
return nil
}
let opml = document.OPMLString(indentLevel: 0, strictConformance: true)
let opmlURL = FileManager.default.temporaryDirectory.appendingPathComponent("NNW3.opml")
do {
try opml.write(to: opmlURL, atomically: true, encoding: .utf8)
} catch let error as NSError {
NSApplication.shared.presentError(error)
return nil
}
return opmlURL
}
}

View File

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15400" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15400"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NNW3OpenPanelAccessoryViewController" customModule="NetNewsWire" customModuleProvider="target">
<connections>
<outlet property="accountPopUpButton" destination="M8B-pG-mg8" id="yAF-ef-1Qn"/>
<outlet property="view" destination="c22-O7-iKe" id="Ir0-h6-xWk"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView misplaced="YES" id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="500" height="133"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="gSF-Ze-XcY">
<rect key="frame" x="50" y="20" width="400" height="77"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="wGx-8H-BqE">
<rect key="frame" x="-2" y="29" width="404" height="48"/>
<textFieldCell key="cell" selectable="YES" id="IFj-4w-B03">
<font key="font" metaFont="system"/>
<string key="title">Choose a NetNewsWire 3 “Subscriptions.plist” file.
Then choose the account to receive your imported subscriptions.</string>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="976-Ry-M6G">
<rect key="frame" x="-2" y="3" width="63" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Account:" id="uoh-QY-7LX">
<font key="font" metaFont="systemBold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="M8B-pG-mg8" userLabel="Account Popup">
<rect key="frame" x="65" y="-3" width="338" height="25"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="OAk-KA-y5i" id="ddF-fN-stL">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<menu key="menu" id="xya-TQ-koA">
<items>
<menuItem title="Item 1" state="on" id="OAk-KA-y5i"/>
<menuItem title="Item 2" id="zkS-Dx-uCM"/>
<menuItem title="Item 3" id="hAU-Zh-UCo"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="M8B-pG-mg8" secondAttribute="trailing" id="0tJ-sp-AWf"/>
<constraint firstItem="wGx-8H-BqE" firstAttribute="top" secondItem="gSF-Ze-XcY" secondAttribute="top" id="1du-tK-vXo"/>
<constraint firstItem="M8B-pG-mg8" firstAttribute="leading" secondItem="976-Ry-M6G" secondAttribute="trailing" constant="8" symbolic="YES" id="HfV-5e-Qa6"/>
<constraint firstAttribute="width" constant="400" id="Pgs-f3-wRX"/>
<constraint firstAttribute="bottom" secondItem="M8B-pG-mg8" secondAttribute="bottom" id="T2s-0I-bPf"/>
<constraint firstItem="M8B-pG-mg8" firstAttribute="top" secondItem="wGx-8H-BqE" secondAttribute="bottom" constant="8" symbolic="YES" id="ZmZ-rR-1NZ"/>
<constraint firstAttribute="trailing" secondItem="wGx-8H-BqE" secondAttribute="trailing" id="aw0-OW-sHK"/>
<constraint firstItem="wGx-8H-BqE" firstAttribute="leading" secondItem="gSF-Ze-XcY" secondAttribute="leading" id="c4P-PJ-vd2"/>
<constraint firstItem="976-Ry-M6G" firstAttribute="leading" secondItem="gSF-Ze-XcY" secondAttribute="leading" id="fME-2N-NXI"/>
<constraint firstItem="M8B-pG-mg8" firstAttribute="firstBaseline" secondItem="976-Ry-M6G" secondAttribute="firstBaseline" id="joR-fP-Zu7"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="gSF-Ze-XcY" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="c22-O7-iKe" secondAttribute="leading" constant="20" symbolic="YES" id="8ME-U9-NVu"/>
<constraint firstAttribute="bottom" secondItem="gSF-Ze-XcY" secondAttribute="bottom" constant="20" symbolic="YES" id="SEy-vf-prc"/>
<constraint firstItem="gSF-Ze-XcY" firstAttribute="centerX" secondItem="c22-O7-iKe" secondAttribute="centerX" id="Z6I-GH-YSw"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="gSF-Ze-XcY" secondAttribute="trailing" constant="20" symbolic="YES" id="lqA-br-FGM"/>
<constraint firstItem="gSF-Ze-XcY" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" constant="20" symbolic="YES" id="mP1-JX-CqQ"/>
</constraints>
<point key="canvasLocation" x="140" y="96"/>
</customView>
</objects>
</document>

View File

@ -0,0 +1,47 @@
//
// NNW3OpenPanelAccessoryViewController.swift
// NetNewsWire
//
// Created by Brent Simmons on 10/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import AppKit
import Account
final class NNW3OpenPanelAccessoryViewController: NSViewController {
@IBOutlet weak var accountPopUpButton: NSPopUpButton!
var selectedAccount: Account? {
accountPopUpButton.selectedItem?.representedObject as? Account
}
init() {
super.init(nibName: "NNW3OpenPanelAccessoryView", bundle: nil)
}
// MARK: - NSViewController
required init?(coder: NSCoder) {
preconditionFailure("NNW3OpenPanelAccessoryViewController.init(coder) not implemented by design.")
}
override func viewDidLoad() {
accountPopUpButton.removeAllItems()
let menu = NSMenu()
accountPopUpButton.menu = menu
for account in AccountManager.shared.sortedActiveAccounts {
let menuItem = NSMenuItem()
menuItem.title = account.nameForDisplay
menuItem.representedObject = account
menu.addItem(menuItem)
if account.accountID == AppDefaults.importOPMLAccountID {
accountPopUpButton.select(menuItem)
}
}
}
}

View File

@ -1,39 +0,0 @@
//
// NNW3Importer.swift
// NetNewsWire
//
// Created by Maurice Parker on 10/14/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import Foundation
class NNW3PlistConverter {
static var defaultFilePath: String {
return ("~/Library/Application Support/NetNewsWire/Subscriptions.plist" as NSString).expandingTildeInPath
}
static func convertToOPML(url: URL) -> URL? {
guard let data = try? Data(contentsOf: url) else {
return nil
}
guard let nnw3plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [[String: AnyObject]] else {
return nil
}
let opmlURL = FileManager.default.temporaryDirectory.appendingPathComponent("NNW3.opml")
let doc = NNW3Document(plist: nnw3plist)
let opml = doc.makeXML(indentLevel: 0)
do {
try opml.write(to: opmlURL, atomically: true, encoding: .utf8)
} catch let error as NSError {
NSApplication.shared.presentError(error)
return nil
}
return opmlURL
}
}

View File

@ -35,8 +35,6 @@
51554C25228B71910055115A /* SyncDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
51554C30228B71A10055115A /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; };
51554C31228B71A10055115A /* SyncDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
516CAC4C235521070038D354 /* ImportNNW3Sheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516CAC42235521070038D354 /* ImportNNW3Sheet.xib */; };
516CAC4D235521070038D354 /* ImportNNW3WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516CAC4B235521070038D354 /* ImportNNW3WindowController.swift */; };
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */; };
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */; };
5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCDC226F1F5C0010922C /* NavigationProgressView.swift */; };
@ -48,11 +46,7 @@
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; };
5183CCED22711DCE0010922C /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5183CCEC22711DCE0010922C /* Settings.storyboard */; };
5183CCEF227125970010922C /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCEE227125970010922C /* SettingsViewController.swift */; };
5186515223552E610078E021 /* NNW3PlistConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515123552E610078E021 /* NNW3PlistConverter.swift */; };
5186515E23552F040078E021 /* NNW3Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515B23552F040078E021 /* NNW3Feed.swift */; };
5186515F23552F040078E021 /* NNW3Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515C23552F040078E021 /* NNW3Document.swift */; };
5186516023552F040078E021 /* NNW3Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515D23552F040078E021 /* NNW3Entry.swift */; };
51865167235556240078E021 /* NNW3FeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51865166235556240078E021 /* NNW3FeedsImporter.swift */; };
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; };
51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -185,8 +179,11 @@
844B5B651FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 844B5B641FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist */; };
844B5B671FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844B5B661FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift */; };
844B5B691FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 844B5B681FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist */; };
844BA21923557D3C00ECF23E /* NNW3ImportController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844BA21823557D3C00ECF23E /* NNW3ImportController.swift */; };
845213231FCA5B11003B6E93 /* ImageDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845213221FCA5B10003B6E93 /* ImageDownloader.swift */; };
845479881FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 845479871FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist */; };
8459D0F92355794C0050076F /* NNW3OpenPanelAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8459D0F82355794C0050076F /* NNW3OpenPanelAccessoryView.xib */; };
8459D10323557A460050076F /* NNW3OpenPanelAccessoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8459D10223557A460050076F /* NNW3OpenPanelAccessoryViewController.swift */; };
845A29091FC74B8E007B49E3 /* SingleFaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29081FC74B8E007B49E3 /* SingleFaviconDownloader.swift */; };
845A29221FC9251E007B49E3 /* SidebarCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29211FC9251E007B49E3 /* SidebarCellLayout.swift */; };
845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29231FC9255E007B49E3 /* SidebarCellAppearance.swift */; };
@ -697,8 +694,6 @@
515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLocalAccountViewController.swift; sourceTree = "<group>"; };
515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = "<group>"; };
51554BFC228B6EB50055115A /* SyncDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SyncDatabase.xcodeproj; path = Frameworks/SyncDatabase/SyncDatabase.xcodeproj; sourceTree = SOURCE_ROOT; };
516CAC42235521070038D354 /* ImportNNW3Sheet.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ImportNNW3Sheet.xib; sourceTree = "<group>"; };
516CAC4B235521070038D354 /* ImportNNW3WindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportNNW3WindowController.swift; sourceTree = "<group>"; };
5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicLabel.swift; sourceTree = "<group>"; };
5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicImageView.swift; sourceTree = "<group>"; };
5183CCDC226F1F5C0010922C /* NavigationProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationProgressView.swift; sourceTree = "<group>"; };
@ -708,11 +703,7 @@
5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = "<group>"; };
5183CCEC22711DCE0010922C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
5183CCEE227125970010922C /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
5186515123552E610078E021 /* NNW3PlistConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNW3PlistConverter.swift; sourceTree = "<group>"; };
5186515B23552F040078E021 /* NNW3Feed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Feed.swift; sourceTree = "<group>"; };
5186515C23552F040078E021 /* NNW3Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Document.swift; sourceTree = "<group>"; };
5186515D23552F040078E021 /* NNW3Entry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Entry.swift; sourceTree = "<group>"; };
51865166235556240078E021 /* NNW3FeedsImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNW3FeedsImporter.swift; sourceTree = "<group>"; };
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = "<group>"; };
51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = "<group>"; };
@ -801,8 +792,11 @@
844B5B641FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = GlobalKeyboardShortcuts.plist; sourceTree = "<group>"; };
844B5B661FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWIndowKeyboardHandler.swift; sourceTree = "<group>"; };
844B5B681FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = SidebarKeyboardShortcuts.plist; sourceTree = "<group>"; };
844BA21823557D3C00ECF23E /* NNW3ImportController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNW3ImportController.swift; sourceTree = "<group>"; };
845213221FCA5B10003B6E93 /* ImageDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageDownloader.swift; sourceTree = "<group>"; };
845479871FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = TimelineKeyboardShortcuts.plist; sourceTree = "<group>"; };
8459D0F82355794C0050076F /* NNW3OpenPanelAccessoryView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NNW3OpenPanelAccessoryView.xib; sourceTree = "<group>"; };
8459D10223557A460050076F /* NNW3OpenPanelAccessoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNW3OpenPanelAccessoryViewController.swift; sourceTree = "<group>"; };
845A29081FC74B8E007B49E3 /* SingleFaviconDownloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleFaviconDownloader.swift; sourceTree = "<group>"; };
845A29211FC9251E007B49E3 /* SidebarCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarCellLayout.swift; sourceTree = "<group>"; };
845A29231FC9255E007B49E3 /* SidebarCellAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarCellAppearance.swift; sourceTree = "<group>"; };
@ -1058,13 +1052,10 @@
516CAC53235529180038D354 /* NNW3 */ = {
isa = PBXGroup;
children = (
516CAC42235521070038D354 /* ImportNNW3Sheet.xib */,
516CAC4B235521070038D354 /* ImportNNW3WindowController.swift */,
51865166235556240078E021 /* NNW3FeedsImporter.swift */,
5186515123552E610078E021 /* NNW3PlistConverter.swift */,
844BA21823557D3C00ECF23E /* NNW3ImportController.swift */,
5186515C23552F040078E021 /* NNW3Document.swift */,
5186515D23552F040078E021 /* NNW3Entry.swift */,
5186515B23552F040078E021 /* NNW3Feed.swift */,
8459D0F82355794C0050076F /* NNW3OpenPanelAccessoryView.xib */,
8459D10223557A460050076F /* NNW3OpenPanelAccessoryViewController.swift */,
);
path = NNW3;
sourceTree = "<group>";
@ -1998,12 +1989,12 @@
ORGANIZATIONNAME = "Ranchero Software";
TargetAttributes = {
6581C73220CED60000F4AD34 = {
DevelopmentTeam = SHJK2V3AJG;
ProvisioningStyle = Automatic;
DevelopmentTeam = M8L2WTLA8W;
ProvisioningStyle = Manual;
};
840D617B2029031C009BC708 = {
CreatedOnToolsVersion = 9.3;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = M8L2WTLA8W;
ProvisioningStyle = Automatic;
SystemCapabilities = {
com.apple.BackgroundModes = {
@ -2019,8 +2010,8 @@
};
849C645F1ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
ProvisioningStyle = Automatic;
DevelopmentTeam = M8L2WTLA8W;
ProvisioningStyle = Manual;
SystemCapabilities = {
com.apple.HardenedRuntime = {
enabled = 1;
@ -2029,7 +2020,7 @@
};
849C64701ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = 9C84TZ7Q6Z;
ProvisioningStyle = Automatic;
TestTargetID = 849C645F1ED37A5D003D8FC0;
};
@ -2284,7 +2275,6 @@
files = (
844B5B651FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist in Resources */,
5127B23A222B4849006D641D /* DetailKeyboardShortcuts.plist in Resources */,
516CAC4C235521070038D354 /* ImportNNW3Sheet.xib in Resources */,
845479881FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist in Resources */,
848362FF2262A30E00DA1D35 /* template.html in Resources */,
848363082262A3DD00DA1D35 /* Main.storyboard in Resources */,
@ -2296,6 +2286,7 @@
5144EA3B227A379E00D19003 /* ImportOPMLSheet.xib in Resources */,
844B5B691FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist in Resources */,
84A3EE5F223B667F00557320 /* DefaultFeeds.opml in Resources */,
8459D0F92355794C0050076F /* NNW3OpenPanelAccessoryView.xib in Resources */,
84C9FC8222629E4800D921D6 /* Preferences.storyboard in Resources */,
5144EA3D227A37AF00D19003 /* ExportOPMLSheet.xib in Resources */,
849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */,
@ -2477,6 +2468,7 @@
841ABA4E20145E7300980E11 /* NothingInspectorViewController.swift in Sources */,
842E45CE1ED8C308000A8B52 /* AppNotifications.swift in Sources */,
844B5B5B1FEA00FB00C7C76A /* TimelineKeyboardDelegate.swift in Sources */,
844BA21923557D3C00ECF23E /* NNW3ImportController.swift in Sources */,
842E45DD1ED8C54B000A8B52 /* Browser.swift in Sources */,
84216D0322128B9D0049B9B9 /* DetailWebViewController.swift in Sources */,
8444C8F21FED81840051386C /* OPMLExporter.swift in Sources */,
@ -2492,11 +2484,9 @@
D5A2678C20130ECF00A8D3C0 /* Author+Scriptability.swift in Sources */,
84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */,
51EF0F902279C9500050506E /* AccountsAddViewController.swift in Sources */,
5186515223552E610078E021 /* NNW3PlistConverter.swift in Sources */,
D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */,
D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */,
845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */,
51865167235556240078E021 /* NNW3FeedsImporter.swift in Sources */,
84702AA41FA27AC0006B8943 /* MarkStatusCommand.swift in Sources */,
D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */,
8405DD9C22153BD7008CE1BF /* NSView-Extensions.swift in Sources */,
@ -2535,7 +2525,6 @@
5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */,
84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */,
845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */,
5186515E23552F040078E021 /* NNW3Feed.swift in Sources */,
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */,
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */,
84C9FC7722629E1200D921D6 /* AdvancedPreferencesViewController.swift in Sources */,
@ -2543,6 +2532,7 @@
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
849EE70F203919360082A1EA /* AppAssets.swift in Sources */,
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */,
8459D10323557A460050076F /* NNW3OpenPanelAccessoryViewController.swift in Sources */,
5183CCE8226F68D90010922C /* AccountRefreshTimer.swift in Sources */,
849A97831ED9EC63007D329B /* SidebarStatusBarView.swift in Sources */,
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */,
@ -2557,7 +2547,6 @@
848D578E21543519005FFAD5 /* PasteboardFeed.swift in Sources */,
5144EA2F2279FAB600D19003 /* AccountsDetailViewController.swift in Sources */,
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,
5186516023552F040078E021 /* NNW3Entry.swift in Sources */,
84C9FC6722629B9000D921D6 /* AppDelegate.swift in Sources */,
84C9FC7A22629E1200D921D6 /* AccountsTableViewBackgroundView.swift in Sources */,
84CAFCAF22BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
@ -2579,7 +2568,6 @@
5144EA40227A37EC00D19003 /* ImportOPMLWindowController.swift in Sources */,
849A976D1ED9EBC8007D329B /* TimelineTableView.swift in Sources */,
84D52E951FE588BB00D14F5B /* DetailStatusBarView.swift in Sources */,
516CAC4D235521070038D354 /* ImportNNW3WindowController.swift in Sources */,
D5E4CC64202C1AC1009B4FFC /* MainWindowController+Scriptability.swift in Sources */,
84C9FC7922629E1200D921D6 /* PreferencesWindowController.swift in Sources */,
84411E711FE5FBFA004B527F /* SmallIconProvider.swift in Sources */,

View File

@ -12,21 +12,10 @@ import RSCore
struct DefaultFeedsImporter {
static func importIfNeeded(_ isFirstRun: Bool, account: Account) {
guard shouldImportDefaultFeeds(isFirstRun) else {
return
}
static func importDefaultFeeds(account: Account) {
appDelegate.logDebugMessage("Importing default feeds.")
let defaultFeedsURL = Bundle.main.url(forResource: "DefaultFeeds", withExtension: "opml")!
AccountManager.shared.defaultAccount.importOPML(defaultFeedsURL) { result in }
}
private static func shouldImportDefaultFeeds(_ isFirstRun: Bool) -> Bool {
if !isFirstRun || AccountManager.shared.anyAccountHasAtLeastOneFeed() {
return false
}
return true
}
}