mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Merge master.
This commit is contained in:
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -1,6 +1,13 @@
|
||||
name: CI
|
||||
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- mac-candidate
|
||||
- mac-release
|
||||
- ios-candidate
|
||||
- ios-release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -6,27 +6,17 @@
|
||||
<description>Most recent NetNewsWire changes with links to updates.</description>
|
||||
<language>en</language>
|
||||
|
||||
<item>
|
||||
<item>
|
||||
<title>NetNewsWire 5.0.3</title>
|
||||
<description><![CDATA[
|
||||
<p>Significantly enhanced performance during syncs and refreshes.</p>
|
||||
|
||||
<p>When running for the first time, and the user previously used NetNewsWire 3, it will automatically import NetNewsWire 3 subscriptions instead of the defaults for new users.</p>
|
||||
|
||||
<p>You can also import NetNewsWire 3 subscriptions via the new File > Import NNW3 Subscriptions… command.</p>
|
||||
|
||||
<p>Fixed the space bar when running on Catalina. It wouldn’t advance to the next unread — now it will. (This was due to a change in JavaScript in Catalina.)</p>
|
||||
|
||||
<p>Fixed a crashing bug having to do with async database fetches for the timeline.</p>
|
||||
|
||||
<p>Periodically empties the articles cache that was added in 5.0.3b1, so its memory use doesn’t just keep expanding.</p>
|
||||
]]></description>
|
||||
<pubDate>Tue, 22 Oct 2019 09:20:00 -0700</pubDate>
|
||||
<enclosure url="https://github.com/brentsimmons/NetNewsWire/releases/download/mac-5.0.3b2/NetNewsWire5.0.3b2.zip" sparkle:version="2617" sparkle:shortVersionString="5.0.3b2" length="5152214" type="application/zip" />
|
||||
<p>Same as 5.0.3b2 — just bumped the version number to 5.0.3.</p>
|
||||
]]></description>
|
||||
<pubDate>Tue, 22 Oct 2019 13:00:00 -0700</pubDate>
|
||||
<enclosure url="https://github.com/brentsimmons/NetNewsWire/releases/download/mac-5.0.3/NetNewsWire5.0.3.zip" sparkle:version="2618" sparkle:shortVersionString="5.0.3" length="5152201" type="application/zip" />
|
||||
<sparkle:minimumSystemVersion>10.14.4</sparkle:minimumSystemVersion>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<item>
|
||||
<title>NetNewsWire 5.0.3b2</title>
|
||||
<description><![CDATA[
|
||||
<p>Significantly enhanced performance during syncs and refreshes.</p>
|
||||
|
||||
@@ -6,6 +6,39 @@
|
||||
<description>Most recent NetNewsWire releases (not test builds). Well, we’re including test builds up until 5.0 ships, but after that it won’t be test builds.</description>
|
||||
<language>en</language>
|
||||
|
||||
<item>
|
||||
<title>NetNewsWire 5.0.3</title>
|
||||
<description><![CDATA[
|
||||
<p>Significantly enhanced performance during syncs and refreshes. Fetching articles from the database is also faster.</p>
|
||||
|
||||
<p>When running for the first time, and the user previously used NetNewsWire 3, it will automatically import NetNewsWire 3 subscriptions instead of the defaults for new users.</p>
|
||||
|
||||
<p>You can also import NetNewsWire 3 subscriptions via the new File > Import NNW3 Subscriptions… command.</p>
|
||||
|
||||
<p>Keyboard shortcuts: the 's' key toggles starred status. The 'r' and 'u' keys now both toggle read status (instead of setting read and unread status, respectively).</p>
|
||||
|
||||
<p>Articles view: articles where the feed icon is quite large would be slow to render — now they render as fast as other articles.</p>
|
||||
|
||||
<p>Articles view: a bug where keyboard shortcuts wouldn’t work after giving the articles view focus has been fixed.</p>
|
||||
|
||||
<p>Articles view: YouTube videos could end up small. Fixed.</p>
|
||||
|
||||
<p>Articles view: fixed a bug scaling images to fit in the view.</p>
|
||||
|
||||
<p>Fixed the space bar when running on Catalina. It wouldn’t advance to the next unread — now it will. (This was due to a change in JavaScript in Catalina.)</p>
|
||||
|
||||
<p>Fixed a crashing bug having to do with async database fetches for the timeline.</p>
|
||||
|
||||
<p>Feedbin syncing: fixed a bug where renaming a tag on the Feedbin site would result in feeds in NNW ending up at the top level.</p>
|
||||
|
||||
<p>Help menu: fixed the expired Slack link.</p>
|
||||
|
||||
]]></description>
|
||||
<pubDate>Tue, 22 Oct 2019 13:00:00 -0700</pubDate>
|
||||
<enclosure url="https://github.com/brentsimmons/NetNewsWire/releases/download/mac-5.0.3/NetNewsWire5.0.3.zip" sparkle:version="2618" sparkle:shortVersionString="5.0.3" length="5152201" type="application/zip" />
|
||||
<sparkle:minimumSystemVersion>10.14.4</sparkle:minimumSystemVersion>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<title>NetNewsWire 5.0.2</title>
|
||||
<description><![CDATA[
|
||||
|
||||
@@ -28,7 +28,7 @@ public enum AccountError: LocalizedError {
|
||||
switch error {
|
||||
case TransportError.httpError(let status):
|
||||
if status == 401 {
|
||||
let localizedText = NSLocalizedString("Your \"%@\" credentials are invalid or expired.", comment: "Invalid or expired")
|
||||
let localizedText = NSLocalizedString("Your “%@” credentials are invalid or expired.", comment: "Invalid or expired")
|
||||
return NSString.localizedStringWithFormat(localizedText as NSString, account.nameForDisplay) as String
|
||||
} else {
|
||||
return unknownError(error, account)
|
||||
@@ -62,7 +62,7 @@ public enum AccountError: LocalizedError {
|
||||
}
|
||||
|
||||
private func unknownError(_ error: Error, _ account: Account) -> String {
|
||||
let localizedText = NSLocalizedString("An error occurred while processing the \"%@\" account: %@", comment: "Unknown error")
|
||||
let localizedText = NSLocalizedString("An error occurred while processing the “%@” account: %@", comment: "Unknown error")
|
||||
return NSString.localizedStringWithFormat(localizedText as NSString, account.nameForDisplay, error.localizedDescription) as String
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,37 +22,37 @@ enum FeedlyAccountDelegateError: LocalizedError {
|
||||
var errorDescription: String? {
|
||||
switch self {
|
||||
case .notLoggedIn:
|
||||
return NSLocalizedString("Please add the Feedly account again.", comment: "Feedly - Credentials not found.")
|
||||
return NSLocalizedString("Please add the Feedly account again.", comment: "Feedly – Credentials not found.")
|
||||
|
||||
case .unableToAddFolder(let name):
|
||||
let template = NSLocalizedString("Could not create a folder named \"%@\".", comment: "Feedly - Could not create a folder/collection.")
|
||||
let template = NSLocalizedString("Could not create a folder named “%@”.", comment: "Feedly – Could not create a folder/collection.")
|
||||
return String(format: template, name)
|
||||
|
||||
case .unableToRenameFolder(let from, let to):
|
||||
let template = NSLocalizedString("Could not rename \"%@\" to \"%@\".", comment: "Feedly - Could not rename a folder/collection.")
|
||||
let template = NSLocalizedString("Could not rename “%@” to “%@”.", comment: "Feedly – Could not rename a folder/collection.")
|
||||
return String(format: template, from, to)
|
||||
|
||||
case .unableToRemoveFolder(let name):
|
||||
let template = NSLocalizedString("Could not remove the folder named \"%@\".", comment: "Feedly - Could not remove a folder/collection.")
|
||||
let template = NSLocalizedString("Could not remove the folder named “%@”.", comment: "Feedly – Could not remove a folder/collection.")
|
||||
return String(format: template, name)
|
||||
|
||||
case .unableToMoveFeedBetweenFolders(let feed, _, let to):
|
||||
let template = NSLocalizedString("Could not move \"%@\" to \"%@\".", comment: "Feedly - Could not move a feed between folders/collections.")
|
||||
let template = NSLocalizedString("Could not move “%@” to “%@”.", comment: "Feedly – Could not move a feed between folders/collections.")
|
||||
return String(format: template, feed.nameForDisplay, to.nameForDisplay)
|
||||
|
||||
case .addFeedChooseFolder:
|
||||
return NSLocalizedString("Please choose a folder to contain the feed.", comment: "Feedly - Feed can only be added to folders.")
|
||||
return NSLocalizedString("Please choose a folder to contain the feed.", comment: "Feedly – Feed can only be added to folders.")
|
||||
|
||||
case .addFeedInvalidFolder(let invalidFolder):
|
||||
let template = NSLocalizedString("Feeds cannot be added to the \"%@\" folder.", comment: "Feedly - Feed can only be added to folders.")
|
||||
let template = NSLocalizedString("Feeds cannot be added to the “%@” folder.", comment: "Feedly – Feed can only be added to folders.")
|
||||
return String(format: template, invalidFolder.nameForDisplay)
|
||||
|
||||
case .unableToRenameFeed(let from, let to):
|
||||
let template = NSLocalizedString("Could not rename \"%@\" to \"%@\".", comment: "Feedly - Could not rename a feed.")
|
||||
let template = NSLocalizedString("Could not rename “%@” to “%@”.", comment: "Feedly – Could not rename a feed.")
|
||||
return String(format: template, from, to)
|
||||
|
||||
case .unableToRemoveFeed(let feed):
|
||||
let template = NSLocalizedString("Could not remove \"%@\".", comment: "Feedly - Could not remove a feed.")
|
||||
let template = NSLocalizedString("Could not remove “%@”.", comment: "Feedly – Could not remove a feed.")
|
||||
return String(format: template, feed.nameForDisplay)
|
||||
}
|
||||
}
|
||||
@@ -72,14 +72,14 @@ enum FeedlyAccountDelegateError: LocalizedError {
|
||||
return nil
|
||||
|
||||
case .unableToMoveFeedBetweenFolders(let feed, let from, let to):
|
||||
let template = NSLocalizedString("\"%@\" may be in both \"%@\" and \"%@\".", comment: "Feedly - Could not move a feed between folders/collections.")
|
||||
let template = NSLocalizedString("“%@” may be in both “%@” and “%@”.", comment: "Feedly – Could not move a feed between folders/collections.")
|
||||
return String(format: template, feed.nameForDisplay, from.nameForDisplay, to.nameForDisplay)
|
||||
|
||||
case .addFeedChooseFolder:
|
||||
return nil
|
||||
|
||||
case .addFeedInvalidFolder:
|
||||
return NSLocalizedString("Please choose a different folder to contain the feed.", comment: "Feedly - Feed can only be added to folders recovery suggestion.")
|
||||
return NSLocalizedString("Please choose a different folder to contain the feed.", comment: "Feedly – Feed can only be added to folders recovery suggestion.")
|
||||
|
||||
case .unableToRemoveFeed:
|
||||
return nil
|
||||
|
||||
@@ -13,14 +13,21 @@ import RSTree
|
||||
import RSWeb
|
||||
import Account
|
||||
import RSCore
|
||||
#if TEST
|
||||
|
||||
// If we're not going to import Sparkle, provide dummy protocols to make it easy
|
||||
// for AppDelegate to comply
|
||||
#if MAC_APP_STORE || TEST
|
||||
protocol SPUStandardUserDriverDelegate {}
|
||||
protocol SPUUpdaterDelegate {}
|
||||
#else
|
||||
import Sparkle
|
||||
#endif
|
||||
|
||||
var appDelegate: AppDelegate!
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider {
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UNUserNotificationCenterDelegate, UnreadCountProvider, SPUStandardUserDriverDelegate, SPUUpdaterDelegate
|
||||
{
|
||||
|
||||
var userNotificationManager: UserNotificationManager!
|
||||
var faviconDownloader: FaviconDownloader!
|
||||
@@ -70,6 +77,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||
private let log = Log()
|
||||
private let appNewsURLString = "https://nnw.ranchero.com/feed.json"
|
||||
private let appMovementMonitor = RSAppMovementMonitor()
|
||||
#if !MAC_APP_STORE && !TEST
|
||||
private var softwareUpdater: SPUUpdater!
|
||||
#endif
|
||||
|
||||
override init() {
|
||||
NSWindow.allowsAutomaticWindowTabbing = false
|
||||
@@ -117,16 +127,24 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||
|
||||
func applicationWillFinishLaunching(_ notification: Notification) {
|
||||
installAppleEventHandlers()
|
||||
#if TEST
|
||||
// Don't prompt for updates while running automated tests
|
||||
SUUpdater.shared()?.automaticallyChecksForUpdates = false
|
||||
#endif
|
||||
}
|
||||
|
||||
func applicationDidFinishLaunching(_ note: Notification) {
|
||||
|
||||
#if MAC_APP_STORE
|
||||
#if MAC_APP_STORE || TEST
|
||||
checkForUpdatesMenuItem.isHidden = true
|
||||
#else
|
||||
// Initialize Sparkle...
|
||||
let hostBundle = Bundle.main
|
||||
let updateDriver = SPUStandardUserDriver(hostBundle: hostBundle, delegate: self)
|
||||
self.softwareUpdater = SPUUpdater(hostBundle: hostBundle, applicationBundle: hostBundle, userDriver: updateDriver, delegate: self)
|
||||
|
||||
do {
|
||||
try self.softwareUpdater.start()
|
||||
}
|
||||
catch {
|
||||
NSLog("Failed to start software updater with error: \(error)")
|
||||
}
|
||||
#endif
|
||||
|
||||
appName = (Bundle.main.infoDictionary!["CFBundleExecutable"]! as! String)
|
||||
@@ -571,7 +589,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||
@IBAction func groupByFeedToggled(_ sender: NSMenuItem) {
|
||||
AppDefaults.timelineGroupByFeed.toggle()
|
||||
}
|
||||
|
||||
|
||||
@IBAction func checkForUpdates(_ sender: Any?) {
|
||||
#if !MAC_APP_STORE && !TEST
|
||||
self.softwareUpdater.checkForUpdates()
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Debug Menu
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<menuItem title="Check for Updates…" id="1nF-7O-aKU">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="checkForUpdates:" target="5Wg-Cw-OOw" id="vV0-oc-v5a"/>
|
||||
<action selector="checkForUpdates:" target="Ady-hI-5gd" id="htt-e3-mEy"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
@@ -623,7 +623,6 @@
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<customObject id="5Wg-Cw-OOw" customClass="SUUpdater"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-54" y="-144"/>
|
||||
</scene>
|
||||
|
||||
@@ -234,17 +234,7 @@
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<binding destination="1hh-8U-B8b" name="value" keyPath="automaticallyChecksForUpdates" id="cge-fo-09y">
|
||||
<dictionary key="options">
|
||||
<bool key="NSAllowsEditingMultipleValuesSelection" value="NO"/>
|
||||
<bool key="NSConditionallySetsEnabled" value="NO"/>
|
||||
<integer key="NSMultipleValuesPlaceholder" value="0"/>
|
||||
<integer key="NSNoSelectionPlaceholder" value="0"/>
|
||||
<integer key="NSNotApplicablePlaceholder" value="0"/>
|
||||
<integer key="NSNullPlaceholder" value="0"/>
|
||||
<bool key="NSRaisesForNotApplicableKeys" value="NO"/>
|
||||
</dictionary>
|
||||
</binding>
|
||||
<binding destination="mV3-0T-XFc" name="value" keyPath="values.SUEnableAutomaticChecks" id="PwC-5l-EQR"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Q6M-Iz-Ypx">
|
||||
@@ -293,7 +283,7 @@
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="checkForUpdates:" target="1hh-8U-B8b" id="RX4-6c-Osn"/>
|
||||
<action selector="checkForUpdates:" target="VX1-M3-K0J" id="WVQ-XY-Czt"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="SUN-k3-ZEb">
|
||||
@@ -382,8 +372,8 @@
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="VX1-M3-K0J" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
<customObject id="1hh-8U-B8b" customClass="SUUpdater"/>
|
||||
<userDefaultsController id="Y8q-yi-F5Z"/>
|
||||
<userDefaultsController id="mV3-0T-XFc"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-29" y="455.5"/>
|
||||
</scene>
|
||||
|
||||
@@ -52,8 +52,8 @@ final class AccountsPreferencesViewController: NSViewController {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .warning
|
||||
let deletePrompt = NSLocalizedString("Delete", comment: "Delete")
|
||||
alert.messageText = "\(deletePrompt) \"\(acctName)\"?"
|
||||
alert.informativeText = NSLocalizedString("Are you sure you want to delete the account \"\(acctName)\"? This can not be undone.", comment: "Delete text")
|
||||
alert.messageText = "\(deletePrompt) “\(acctName)”?"
|
||||
alert.informativeText = NSLocalizedString("Are you sure you want to delete the account “\(acctName)”? This can not be undone.", comment: "Delete text")
|
||||
|
||||
alert.addButton(withTitle: NSLocalizedString("Delete", comment: "Delete Account"))
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel Delete Account"))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "accountLocal.pdf"
|
||||
"filename" : "localAccountMac.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
Binary file not shown.
@@ -9,7 +9,6 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */; };
|
||||
51102165233A7D6C0007A5F7 /* ArticleExtractorButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */; };
|
||||
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
|
||||
@@ -43,15 +42,8 @@
|
||||
513146C5235A8FDB00387FDC /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; };
|
||||
51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; };
|
||||
51314705235C41FC00387FDC /* Intents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 51314707235C41FC00387FDC /* Intents.intentdefinition */; };
|
||||
51314716235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51314715235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift */; };
|
||||
51314718235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51314717235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift */; };
|
||||
51322855232EED360033D4ED /* VibrantSelectAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51322854232EED360033D4ED /* VibrantSelectAction.swift */; };
|
||||
51322859232FDDB80033D4ED /* VibrantButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */; };
|
||||
5132285B232FF2C40033D4ED /* SettingsRefreshSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132285A232FF2C40033D4ED /* SettingsRefreshSelectionView.swift */; };
|
||||
513228FB233037630033D4ED /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; };
|
||||
513228FC233037630033D4ED /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513228F2233037620033D4ED /* Reachability.swift */; };
|
||||
513229312330523F0033D4ED /* AttributedStringView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513229302330523F0033D4ED /* AttributedStringView.swift */; };
|
||||
5132293B23305D4C0033D4ED /* SettingsAboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5132293A23305D4C0033D4ED /* SettingsAboutView.swift */; };
|
||||
513C5CE9232571C2003D4054 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513C5CE8232571C2003D4054 /* ShareViewController.swift */; };
|
||||
513C5CEC232571C2003D4054 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 513C5CEA232571C2003D4054 /* MainInterface.storyboard */; };
|
||||
513C5CF0232571C2003D4054 /* NetNewsWire iOS Share Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 513C5CE6232571C2003D4054 /* NetNewsWire iOS Share Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
@@ -81,8 +73,6 @@
|
||||
5148F4552336DB7000F8CD8B /* MasterTimelineTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5148F4542336DB7000F8CD8B /* MasterTimelineTitleView.swift */; };
|
||||
514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7C8223205EFB00BAC947 /* RootSplitViewController.swift */; };
|
||||
514B7D1F23219F3C00BAC947 /* AddControllerType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514B7D1E23219F3C00BAC947 /* AddControllerType.swift */; };
|
||||
5152E0F923248F6200E5C7AD /* SettingsLocalAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */; };
|
||||
5152E1022324900D00E5C7AD /* SettingsAddAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */; };
|
||||
5154368B229404D1005E1CDF /* FaviconGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EF0F76227716200050506E /* FaviconGenerator.swift */; };
|
||||
51554C24228B71910055115A /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; };
|
||||
51554C25228B71910055115A /* SyncDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@@ -91,6 +81,11 @@
|
||||
515D4FC123257A3200EE1167 /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; };
|
||||
515D4FCA23257CB500EE1167 /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; };
|
||||
515D4FCC2325815A00EE1167 /* SafariExt.js in Resources */ = {isa = PBXBuildFile; fileRef = 515D4FCB2325815A00EE1167 /* SafariExt.js */; };
|
||||
516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */; };
|
||||
516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */; };
|
||||
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */; };
|
||||
516A09402361240900EAE89B /* Account.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A093F2361240900EAE89B /* Account.storyboard */; };
|
||||
516A09422361248000EAE89B /* Inspector.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 516A09412361248000EAE89B /* Inspector.storyboard */; };
|
||||
51707439232AA97100A461A3 /* ShareFolderPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */; };
|
||||
5170743A232AABFC00A461A3 /* FlattenedAccountFolderPickerData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C452812265093600C03939 /* FlattenedAccountFolderPickerData.swift */; };
|
||||
517630042336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; };
|
||||
@@ -106,20 +101,23 @@
|
||||
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; };
|
||||
518651B223555EB20078E021 /* NNW3Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651AB23555EB20078E021 /* NNW3Document.swift */; };
|
||||
518651DA235621840078E021 /* ImageTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 518651D9235621840078E021 /* ImageTransition.swift */; };
|
||||
5186A635235EF3A800C97195 /* VibrantLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186A634235EF3A800C97195 /* VibrantLabel.swift */; };
|
||||
518B2EE82351B45600400001 /* NetNewsWire_iOSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 840D61952029031D009BC708 /* NetNewsWire_iOSTests.swift */; };
|
||||
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CC1230F5963006127BE /* ThemedNavigationController.swift */; };
|
||||
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; };
|
||||
51938DF2231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
|
||||
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */; };
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
|
||||
519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F35D0822AFD4760003CE1B /* SettingsView.swift */; };
|
||||
519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */; };
|
||||
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */; };
|
||||
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */; };
|
||||
519E743D22C663F900A78E47 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519E743422C663F900A78E47 /* SceneDelegate.swift */; };
|
||||
51AF45E123246731001742EF /* SettingsAccountLabelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */; };
|
||||
51AF460323247321001742EF /* SettingsDetailAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */; };
|
||||
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */; };
|
||||
51A16997235E10D700EB091F /* RefreshIntervalViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */; };
|
||||
51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A1698F235E10D600EB091F /* LocalAccountViewController.swift */; };
|
||||
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51A16990235E10D600EB091F /* Settings.storyboard */; };
|
||||
51A1699B235E10D700EB091F /* AccountInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16991235E10D600EB091F /* AccountInspectorViewController.swift */; };
|
||||
51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16992235E10D600EB091F /* AddAccountViewController.swift */; };
|
||||
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16993235E10D600EB091F /* SettingsViewController.swift */; };
|
||||
51A1699F235E10D700EB091F /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16995235E10D600EB091F /* AboutViewController.swift */; };
|
||||
51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */; };
|
||||
51AF460E232488C6001742EF /* Account-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51AF460D232488C6001742EF /* Account-Extensions.swift */; };
|
||||
51B62E68233186730085F949 /* MasterTimelineAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B62E67233186730085F949 /* MasterTimelineAvatarView.swift */; };
|
||||
51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */; };
|
||||
@@ -199,9 +197,6 @@
|
||||
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
||||
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */; };
|
||||
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; };
|
||||
51E149B3234D82E40004F7A5 /* PasswordField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E149B2234D82E40004F7A5 /* PasswordField.swift */; };
|
||||
51E149C0234D839E0004F7A5 /* ShowHidePasswordView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51E149BF234D839E0004F7A5 /* ShowHidePasswordView.xib */; };
|
||||
51E149C2234D852F0004F7A5 /* ShowHidePasswordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E149C1234D852F0004F7A5 /* ShowHidePasswordView.swift */; };
|
||||
51E3EB33229AB02C00645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB32229AB02C00645299 /* ErrorHandler.swift */; };
|
||||
51E3EB3D229AB08300645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB3C229AB08300645299 /* ErrorHandler.swift */; };
|
||||
51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */; };
|
||||
@@ -239,9 +234,10 @@
|
||||
51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
|
||||
51FE10092346739D0056195D /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; };
|
||||
51FE100A234673A00056195D /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; };
|
||||
51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FFF0C3235EE8E5002762AA /* VibrantButton.swift */; };
|
||||
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */; };
|
||||
55E15BCC229D65A900D6602A /* AccountsReaderAPIWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */; };
|
||||
5F323809231DF9F000706F6B /* NNWTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */; };
|
||||
5F323809231DF9F000706F6B /* VibrantTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */; };
|
||||
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 */; };
|
||||
@@ -622,7 +618,6 @@
|
||||
D5F4EDB720074D6500B9E363 /* Feed+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */; };
|
||||
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */; };
|
||||
DD82AB0A231003F6002269DF /* SharingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD82AB09231003F6002269DF /* SharingTests.swift */; };
|
||||
DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF999FF622B5AEFA0064B687 /* SafariView.swift */; };
|
||||
FF3ABF13232599810074C542 /* ArticleSorterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF09232599450074C542 /* ArticleSorterTests.swift */; };
|
||||
FF3ABF1523259DDB0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
|
||||
FF3ABF162325AF5D0074C542 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
|
||||
@@ -1200,10 +1195,6 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = "<group>"; };
|
||||
510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddAccountView.swift; sourceTree = "<group>"; };
|
||||
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = "<group>"; };
|
||||
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFeedbinAccountView.swift; sourceTree = "<group>"; };
|
||||
510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountLabelView.swift; sourceTree = "<group>"; };
|
||||
51102164233A7D6C0007A5F7 /* ArticleExtractorButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleExtractorButton.swift; sourceTree = "<group>"; };
|
||||
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
|
||||
51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContainerViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -1224,14 +1215,7 @@
|
||||
513146B1235A81A400387FDC /* AddFeedIntentHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddFeedIntentHandler.swift; sourceTree = "<group>"; };
|
||||
51314706235C41FC00387FDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intents.intentdefinition; sourceTree = "<group>"; };
|
||||
51314714235C420900387FDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Intents.strings; sourceTree = "<group>"; };
|
||||
51314715235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportAccountPickerView.swift; sourceTree = "<group>"; };
|
||||
51314717235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportAccountPickerView.swift; sourceTree = "<group>"; };
|
||||
51322854232EED360033D4ED /* VibrantSelectAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantSelectAction.swift; sourceTree = "<group>"; };
|
||||
51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantButtonStyle.swift; sourceTree = "<group>"; };
|
||||
5132285A232FF2C40033D4ED /* SettingsRefreshSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsRefreshSelectionView.swift; sourceTree = "<group>"; };
|
||||
513228F2233037620033D4ED /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = "<group>"; };
|
||||
513229302330523F0033D4ED /* AttributedStringView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringView.swift; sourceTree = "<group>"; };
|
||||
5132293A23305D4C0033D4ED /* SettingsAboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAboutView.swift; sourceTree = "<group>"; };
|
||||
513C5CE6232571C2003D4054 /* NetNewsWire iOS Share Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "NetNewsWire iOS Share Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
513C5CE8232571C2003D4054 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
|
||||
513C5CEB232571C2003D4054 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
|
||||
@@ -1257,6 +1241,11 @@
|
||||
515D4FCB2325815A00EE1167 /* SafariExt.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SafariExt.js; sourceTree = "<group>"; };
|
||||
515D4FCD2325909200EE1167 /* NetNewsWire_iOS_ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NetNewsWire_iOS_ShareExtension.entitlements; sourceTree = "<group>"; };
|
||||
515D4FCE2325B3D000EE1167 /* NetNewsWire_iOSshareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSshareextension_target.xcconfig; sourceTree = "<group>"; };
|
||||
516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsAccountTableViewCell.xib; sourceTree = "<group>"; };
|
||||
516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAccountTableViewCell.swift; sourceTree = "<group>"; };
|
||||
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SettingsTableViewCell.xib; sourceTree = "<group>"; };
|
||||
516A093F2361240900EAE89B /* Account.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Account.storyboard; sourceTree = "<group>"; };
|
||||
516A09412361248000EAE89B /* Inspector.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Inspector.storyboard; sourceTree = "<group>"; };
|
||||
51707438232AA97100A461A3 /* ShareFolderPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFolderPickerController.swift; sourceTree = "<group>"; };
|
||||
517630032336215100E15FFF /* main.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = main.js; sourceTree = "<group>"; };
|
||||
517630222336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleViewControllerWebViewProvider.swift; sourceTree = "<group>"; };
|
||||
@@ -1268,16 +1257,23 @@
|
||||
5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = "<group>"; };
|
||||
518651AB23555EB20078E021 /* NNW3Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Document.swift; sourceTree = "<group>"; };
|
||||
518651D9235621840078E021 /* ImageTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageTransition.swift; sourceTree = "<group>"; };
|
||||
5186A634235EF3A800C97195 /* VibrantLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantLabel.swift; sourceTree = "<group>"; };
|
||||
518B2ED22351B3DD00400001 /* NetNewsWire-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "NetNewsWire-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
518B2EE92351B4C200400001 /* NetNewsWire_iOSTests_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSTests_target.xcconfig; sourceTree = "<group>"; };
|
||||
51934CC1230F5963006127BE /* ThemedNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedNavigationController.swift; sourceTree = "<group>"; };
|
||||
51934CCD2310792F006127BE /* ActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityManager.swift; sourceTree = "<group>"; };
|
||||
51938DF1231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTimelineFeedDelegate.swift; sourceTree = "<group>"; };
|
||||
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsImportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSubscriptionsExportDocumentPickerView.swift; sourceTree = "<group>"; };
|
||||
519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = "<group>"; };
|
||||
519D740523243CC0008BB345 /* RefreshInterval-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RefreshInterval-Extensions.swift"; sourceTree = "<group>"; };
|
||||
519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RefreshIntervalViewController.swift; sourceTree = "<group>"; };
|
||||
51A1698F235E10D600EB091F /* LocalAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A16990235E10D600EB091F /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
|
||||
51A16991235E10D600EB091F /* AccountInspectorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountInspectorViewController.swift; sourceTree = "<group>"; };
|
||||
51A16992235E10D600EB091F /* AddAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A16993235E10D600EB091F /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
|
||||
51A16995235E10D600EB091F /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51B62E67233186730085F949 /* MasterTimelineAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineAvatarView.swift; sourceTree = "<group>"; };
|
||||
51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = "<group>"; };
|
||||
@@ -1306,9 +1302,6 @@
|
||||
51CC9B3D231720B2000E842F /* MasterFeedDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSource.swift; sourceTree = "<group>"; };
|
||||
51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineDataSource.swift; sourceTree = "<group>"; };
|
||||
51D87EE02311D34700E63F03 /* ActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityType.swift; sourceTree = "<group>"; };
|
||||
51E149B2234D82E40004F7A5 /* PasswordField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordField.swift; sourceTree = "<group>"; };
|
||||
51E149BF234D839E0004F7A5 /* ShowHidePasswordView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ShowHidePasswordView.xib; sourceTree = "<group>"; };
|
||||
51E149C1234D852F0004F7A5 /* ShowHidePasswordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowHidePasswordView.swift; sourceTree = "<group>"; };
|
||||
51E3EB32229AB02C00645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
||||
51E3EB3C229AB08300645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
||||
51E595A4228CC36500FCC42B /* ArticleStatusSyncTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleStatusSyncTimer.swift; sourceTree = "<group>"; };
|
||||
@@ -1322,8 +1315,6 @@
|
||||
51EF0F8D2279C9260050506E /* AccountsAdd.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsAdd.xib; sourceTree = "<group>"; };
|
||||
51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddViewController.swift; sourceTree = "<group>"; };
|
||||
51EF0F912279CA620050506E /* AccountsAddTableCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddTableCellView.swift; sourceTree = "<group>"; };
|
||||
51F35D0822AFD4760003CE1B /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsDetailAccountView.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>"; };
|
||||
@@ -1341,10 +1332,10 @@
|
||||
51FD40BD2341555600880194 /* UIImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage-Extensions.swift"; sourceTree = "<group>"; };
|
||||
51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineUnreadCountView.swift; sourceTree = "<group>"; };
|
||||
51FE10022345529D0056195D /* UserNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationManager.swift; sourceTree = "<group>"; };
|
||||
557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsReaderAPIAccountView.swift; sourceTree = "<group>"; };
|
||||
51FFF0C3235EE8E5002762AA /* VibrantButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantButton.swift; sourceTree = "<group>"; };
|
||||
55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsReaderAPI.xib; sourceTree = "<group>"; };
|
||||
55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsReaderAPIWindowController.swift; sourceTree = "<group>"; };
|
||||
5F323808231DF9F000706F6B /* NNWTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNWTableViewCell.swift; sourceTree = "<group>"; };
|
||||
5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VibrantTableViewCell.swift; sourceTree = "<group>"; };
|
||||
6543108B2322D90900658221 /* common */ = {isa = PBXFileReference; lastKnownFileType = folder; path = common; 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; };
|
||||
@@ -1547,7 +1538,6 @@
|
||||
D5F4EDB620074D6500B9E363 /* Feed+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Feed+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
D5F4EDB820074D7C00B9E363 /* Folder+Scriptability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Folder+Scriptability.swift"; sourceTree = "<group>"; };
|
||||
DD82AB09231003F6002269DF /* SharingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SharingTests.swift; sourceTree = "<group>"; };
|
||||
DF999FF622B5AEFA0064B687 /* SafariView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariView.swift; sourceTree = "<group>"; };
|
||||
FF3ABF09232599450074C542 /* ArticleSorterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorterTests.swift; sourceTree = "<group>"; };
|
||||
FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleSorter.swift; sourceTree = "<group>"; };
|
||||
FFD43E372340F320009E5CA3 /* UndoAvailableAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UndoAvailableAlertController.swift; sourceTree = "<group>"; };
|
||||
@@ -1682,6 +1672,8 @@
|
||||
5123DB95233EC69300282CC9 /* Inspector */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
516A09412361248000EAE89B /* Inspector.storyboard */,
|
||||
51A16991235E10D600EB091F /* AccountInspectorViewController.swift */,
|
||||
5123DB9E233EC6FD00282CC9 /* FeedInspectorView.swift */,
|
||||
);
|
||||
path = Inspector;
|
||||
@@ -1764,15 +1756,12 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
515E4F06232506240057B0E7 /* Account */ = {
|
||||
516A093E236123A800EAE89B /* Account */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
510D708122B041CC004E8F65 /* SettingsAccountLabelView.swift */,
|
||||
510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */,
|
||||
51F772EC22B2789B0087D9D1 /* SettingsDetailAccountView.swift */,
|
||||
510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */,
|
||||
510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */,
|
||||
557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */,
|
||||
516A093F2361240900EAE89B /* Account.storyboard */,
|
||||
51A1698F235E10D600EB091F /* LocalAccountViewController.swift */,
|
||||
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */,
|
||||
);
|
||||
path = Account;
|
||||
sourceTree = "<group>";
|
||||
@@ -1799,14 +1788,14 @@
|
||||
5183CCEB227117C70010922C /* Settings */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
51F35D0822AFD4760003CE1B /* SettingsView.swift */,
|
||||
5132293A23305D4C0033D4ED /* SettingsAboutView.swift */,
|
||||
5132285A232FF2C40033D4ED /* SettingsRefreshSelectionView.swift */,
|
||||
51314717235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift */,
|
||||
5194B5F122B69FCC00144881 /* SettingsSubscriptionsExportDocumentPickerView.swift */,
|
||||
51314715235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift */,
|
||||
5194B5ED22B6965300144881 /* SettingsSubscriptionsImportDocumentPickerView.swift */,
|
||||
515E4F06232506240057B0E7 /* Account */,
|
||||
51A16990235E10D600EB091F /* Settings.storyboard */,
|
||||
51A16995235E10D600EB091F /* AboutViewController.swift */,
|
||||
51A16992235E10D600EB091F /* AddAccountViewController.swift */,
|
||||
51A1698D235E10D600EB091F /* RefreshIntervalViewController.swift */,
|
||||
516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */,
|
||||
516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */,
|
||||
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */,
|
||||
51A16993235E10D600EB091F /* SettingsViewController.swift */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
@@ -1831,20 +1820,6 @@
|
||||
path = Activity;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5194B5E222B693EC00144881 /* SwiftUI Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
513229302330523F0033D4ED /* AttributedStringView.swift */,
|
||||
DF999FF622B5AEFA0064B687 /* SafariView.swift */,
|
||||
51322858232FDDB80033D4ED /* VibrantButtonStyle.swift */,
|
||||
51322854232EED360033D4ED /* VibrantSelectAction.swift */,
|
||||
51E149B2234D82E40004F7A5 /* PasswordField.swift */,
|
||||
51E149C1234D852F0004F7A5 /* ShowHidePasswordView.swift */,
|
||||
51E149BF234D839E0004F7A5 /* ShowHidePasswordView.xib */,
|
||||
);
|
||||
path = "SwiftUI Extensions";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
519D740423243C68008BB345 /* Model Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -1859,7 +1834,6 @@
|
||||
children = (
|
||||
51F85BFA2275D85000C787DC /* Array-Extensions.swift */,
|
||||
51F85BF42273625800C787DC /* Bundle-Extensions.swift */,
|
||||
5F323808231DF9F000706F6B /* NNWTableViewCell.swift */,
|
||||
51EAED95231363EF00A9EEE3 /* NonIntrinsicButton.swift */,
|
||||
5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */,
|
||||
5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */,
|
||||
@@ -1870,6 +1844,9 @@
|
||||
51FD40BD2341555600880194 /* UIImage-Extensions.swift */,
|
||||
512E092B2268B25500BDCFDD /* UISplitViewController-Extensions.swift */,
|
||||
51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */,
|
||||
51FFF0C3235EE8E5002762AA /* VibrantButton.swift */,
|
||||
5186A634235EF3A800C97195 /* VibrantLabel.swift */,
|
||||
5F323808231DF9F000706F6B /* VibrantTableViewCell.swift */,
|
||||
);
|
||||
path = "UIKit Extensions";
|
||||
sourceTree = "<group>";
|
||||
@@ -2557,13 +2534,13 @@
|
||||
51C4525D226508F600C03939 /* MasterFeed */,
|
||||
51C4526D2265091600C03939 /* MasterTimeline */,
|
||||
51C4527D2265092C00C03939 /* Article */,
|
||||
516A093E236123A800EAE89B /* Account */,
|
||||
51C452802265093600C03939 /* Add */,
|
||||
5123DB95233EC69300282CC9 /* Inspector */,
|
||||
513145F9235A55A700387FDC /* Intents */,
|
||||
5183CCDB226F1EEB0010922C /* Progress */,
|
||||
5183CCEB227117C70010922C /* Settings */,
|
||||
519D740423243C68008BB345 /* Model Extensions */,
|
||||
5194B5E222B693EC00144881 /* SwiftUI Extensions */,
|
||||
51C45245226506C800C03939 /* UIKit Extensions */,
|
||||
513C5CE7232571C2003D4054 /* ShareExtension */,
|
||||
51314643235A7C2300387FDC /* IntentsExtension */,
|
||||
@@ -2953,6 +2930,14 @@
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
65ED3FA2235DEF6C0081F399 = {
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
65ED4090235DEF770081F399 = {
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
840D617B2029031C009BC708 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
DevelopmentTeam = SHJK2V3AJG;
|
||||
@@ -3402,16 +3387,20 @@
|
||||
511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */,
|
||||
84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */,
|
||||
51BB7C312335ACDE008E8144 /* page.html in Resources */,
|
||||
516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */,
|
||||
51F85BF32272531500C787DC /* Dedication.rtf in Resources */,
|
||||
516A09422361248000EAE89B /* Inspector.storyboard in Resources */,
|
||||
84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */,
|
||||
51F85BEB22724CB600C787DC /* About.rtf in Resources */,
|
||||
51F85BED227251DF00C787DC /* Acknowledgments.rtf in Resources */,
|
||||
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */,
|
||||
511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */,
|
||||
516A09402361240900EAE89B /* Account.storyboard in Resources */,
|
||||
51C452AB22650DC600C03939 /* template.html in Resources */,
|
||||
51E149C0234D839E0004F7A5 /* ShowHidePasswordView.xib in Resources */,
|
||||
51F85BF12272524100C787DC /* Credits.rtf in Resources */,
|
||||
84A3EE61223B667F00557320 /* DefaultFeeds.opml in Resources */,
|
||||
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */,
|
||||
51A1699A235E10D700EB091F /* Settings.storyboard in Resources */,
|
||||
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */,
|
||||
51F85BEF2272520B00C787DC /* Thanks.rtf in Resources */,
|
||||
84C9FC9D2262A1A900D921D6 /* Assets.xcassets in Resources */,
|
||||
@@ -3881,14 +3870,13 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
840D617F2029031C009BC708 /* AppDelegate.swift in Sources */,
|
||||
51E149B3234D82E40004F7A5 /* PasswordField.swift in Sources */,
|
||||
512E08E72268801200BDCFDD /* FeedTreeControllerDelegate.swift in Sources */,
|
||||
51C452A422650A2D00C03939 /* ArticleUtilities.swift in Sources */,
|
||||
51EF0F79227716380050506E /* ColorHash.swift in Sources */,
|
||||
5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */,
|
||||
51EAED96231363EF00A9EEE3 /* NonIntrinsicButton.swift in Sources */,
|
||||
51C4527B2265091600C03939 /* MasterUnreadIndicatorView.swift in Sources */,
|
||||
5152E1022324900D00E5C7AD /* SettingsAddAccountView.swift in Sources */,
|
||||
5186A635235EF3A800C97195 /* VibrantLabel.swift in Sources */,
|
||||
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
|
||||
51B62E68233186730085F949 /* MasterTimelineAvatarView.swift in Sources */,
|
||||
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */,
|
||||
@@ -3902,40 +3890,34 @@
|
||||
51FD413B2342BD0500880194 /* MasterTimelineUnreadCountView.swift in Sources */,
|
||||
513146B2235A81A400387FDC /* AddFeedIntentHandler.swift in Sources */,
|
||||
5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */,
|
||||
51AF45E123246731001742EF /* SettingsAccountLabelView.swift in Sources */,
|
||||
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */,
|
||||
51C452772265091600C03939 /* MultilineUILabelSizer.swift in Sources */,
|
||||
51C452A522650A2D00C03939 /* SmallIconProvider.swift in Sources */,
|
||||
516A09392360A2AE00EAE89B /* SettingsAccountTableViewCell.swift in Sources */,
|
||||
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */,
|
||||
51322859232FDDB80033D4ED /* VibrantButtonStyle.swift in Sources */,
|
||||
51A1699C235E10D700EB091F /* AddAccountViewController.swift in Sources */,
|
||||
51A16999235E10D700EB091F /* LocalAccountViewController.swift in Sources */,
|
||||
514B7C8323205EFB00BAC947 /* RootSplitViewController.swift in Sources */,
|
||||
5152E0F923248F6200E5C7AD /* SettingsLocalAccountView.swift in Sources */,
|
||||
51FA73A52332BE110090D516 /* ArticleExtractor.swift in Sources */,
|
||||
51314704235C41FC00387FDC /* Intents.intentdefinition in Sources */,
|
||||
FF3ABF162325AF5D0074C542 /* ArticleSorter.swift in Sources */,
|
||||
510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */,
|
||||
51C4525C226508DF00C03939 /* String-Extensions.swift in Sources */,
|
||||
51C452792265091600C03939 /* MasterTimelineTableViewCell.swift in Sources */,
|
||||
51FA73AB2332C2FD0090D516 /* ArticleExtractorConfig.swift in Sources */,
|
||||
5132285B232FF2C40033D4ED /* SettingsRefreshSelectionView.swift in Sources */,
|
||||
51C452852265093600C03939 /* FlattenedAccountFolderPickerData.swift in Sources */,
|
||||
51C4526B226508F600C03939 /* MasterFeedViewController.swift in Sources */,
|
||||
5126EE97226CB48A00C22AFC /* SceneCoordinator.swift in Sources */,
|
||||
51FD40C72341555A00880194 /* UIImage-Extensions.swift in Sources */,
|
||||
5132293B23305D4C0033D4ED /* SettingsAboutView.swift in Sources */,
|
||||
84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
|
||||
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
|
||||
51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */,
|
||||
51C4525A226508D600C03939 /* UIStoryboard-Extensions.swift in Sources */,
|
||||
519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */,
|
||||
51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */,
|
||||
51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */,
|
||||
51F85BF52273625800C787DC /* Bundle-Extensions.swift in Sources */,
|
||||
519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */,
|
||||
51C452A622650A3500C03939 /* Node-Extensions.swift in Sources */,
|
||||
5183CCDF226F1FCC0010922C /* UINavigationController+Progress.swift in Sources */,
|
||||
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */,
|
||||
5F323809231DF9F000706F6B /* NNWTableViewCell.swift in Sources */,
|
||||
5F323809231DF9F000706F6B /* VibrantTableViewCell.swift in Sources */,
|
||||
512E09352268B25900BDCFDD /* UISplitViewController-Extensions.swift in Sources */,
|
||||
51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */,
|
||||
51C452A022650A1900C03939 /* FeedIconDownloader.swift in Sources */,
|
||||
@@ -3953,7 +3935,6 @@
|
||||
51C4529A22650A0400C03939 /* ArticleStyle.swift in Sources */,
|
||||
51C4527F2265092C00C03939 /* ArticleViewController.swift in Sources */,
|
||||
51C4526A226508F600C03939 /* MasterFeedTableViewCellLayout.swift in Sources */,
|
||||
51E149C2234D852F0004F7A5 /* ShowHidePasswordView.swift in Sources */,
|
||||
51C452AE2265104D00C03939 /* ArticleStringFormatter.swift in Sources */,
|
||||
512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */,
|
||||
51C4529922650A0000C03939 /* ArticleStylesManager.swift in Sources */,
|
||||
@@ -3966,28 +3947,28 @@
|
||||
84CAFCA522BC8C08007694F0 /* FetchRequestQueue.swift in Sources */,
|
||||
51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */,
|
||||
51E595A6228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
|
||||
51A1699F235E10D700EB091F /* AboutViewController.swift in Sources */,
|
||||
51C45290226509C100C03939 /* PseudoFeed.swift in Sources */,
|
||||
51C452A922650DC600C03939 /* ArticleRenderer.swift in Sources */,
|
||||
51AF460323247321001742EF /* SettingsDetailAccountView.swift in Sources */,
|
||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */,
|
||||
51C45297226509E300C03939 /* DefaultFeedsImporter.swift in Sources */,
|
||||
512E094D2268B8AB00BDCFDD /* DeleteCommand.swift in Sources */,
|
||||
51F85BFB2275D85000C787DC /* Array-Extensions.swift in Sources */,
|
||||
51C452AC22650FD200C03939 /* AppNotifications.swift in Sources */,
|
||||
51EF0F7E2277A57D0050506E /* MasterTimelineAccessibilityCellLayout.swift in Sources */,
|
||||
51A1699B235E10D700EB091F /* AccountInspectorViewController.swift in Sources */,
|
||||
51C452762265091600C03939 /* MasterTimelineViewController.swift in Sources */,
|
||||
51314718235C89ED00387FDC /* SettingsSubscriptionsExportAccountPickerView.swift in Sources */,
|
||||
5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */,
|
||||
51C452882265093600C03939 /* AddFeedViewController.swift in Sources */,
|
||||
51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */,
|
||||
51934CCE2310792F006127BE /* ActivityManager.swift in Sources */,
|
||||
518651DA235621840078E021 /* ImageTransition.swift in Sources */,
|
||||
514219372352510100E07E2C /* ImageScrollView.swift in Sources */,
|
||||
DF999FF722B5AEFA0064B687 /* SafariView.swift in Sources */,
|
||||
51A16997235E10D700EB091F /* RefreshIntervalViewController.swift in Sources */,
|
||||
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,
|
||||
84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */,
|
||||
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */,
|
||||
519D740623243CC0008BB345 /* RefreshInterval-Extensions.swift in Sources */,
|
||||
51322855232EED360033D4ED /* VibrantSelectAction.swift in Sources */,
|
||||
51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */,
|
||||
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */,
|
||||
51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */,
|
||||
@@ -3998,15 +3979,14 @@
|
||||
51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */,
|
||||
51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */,
|
||||
5148F4552336DB7000F8CD8B /* MasterTimelineTitleView.swift in Sources */,
|
||||
51FFF0C4235EE8E5002762AA /* VibrantButton.swift in Sources */,
|
||||
513228FC233037630033D4ED /* Reachability.swift in Sources */,
|
||||
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
|
||||
519D73FB2323FF35008BB345 /* SettingsView.swift in Sources */,
|
||||
511D4419231FC02D00FB1562 /* KeyboardManager.swift in Sources */,
|
||||
51A1699D235E10D700EB091F /* SettingsViewController.swift in Sources */,
|
||||
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
|
||||
513229312330523F0033D4ED /* AttributedStringView.swift in Sources */,
|
||||
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,
|
||||
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */,
|
||||
51314716235C862200387FDC /* SettingsSubscriptionsImportAccountPickerView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ See the [Contributing](CONTRIBUTING.md) page for our process. It’s pretty stra
|
||||
```bash
|
||||
git clone https://github.com/brentsimmons/NetNewsWire.git
|
||||
cd NetNewsWire
|
||||
git submodule update --init
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
You can locally override the Xcode settings for code signing
|
||||
|
||||
260
iOS/Account/Account.storyboard
Normal file
260
iOS/Account/Account.storyboard
Normal file
@@ -0,0 +1,260 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15508"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="98f-PW-S1C">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="AddLocalAccountNavigationViewController" id="TMY-HB-vAu" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="p8g-7e-3f4">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="vi3-jb-8XS" kind="relationship" relationship="rootViewController" id="dIe-7d-ZQX"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="6sV-68-OXu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2506" y="-528"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="6i4-ho-e4F">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="FeedbinAccountNavigationViewController" id="sFg-MZ-PqJ" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="wq6-np-tNn">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="ECy-jg-Kyc" kind="relationship" relationship="rootViewController" id="usT-8C-GGf"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Lfz-4s-0Vn" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3177" y="-528"/>
|
||||
</scene>
|
||||
<!--On My Device-->
|
||||
<scene sceneID="J93-FN-Yey">
|
||||
<objects>
|
||||
<tableViewController id="vi3-jb-8XS" customClass="LocalAccountViewController" 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="YLa-nM-G7t">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<sections>
|
||||
<tableViewSection id="TfM-Jc-Fr0">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="uFU-j6-qP1">
|
||||
<rect key="frame" x="0.0" y="18" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uFU-j6-qP1" id="fr4-mL-3Yf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Yl1-R6-xZi">
|
||||
<rect key="frame" x="20" y="11.5" width="374" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Yl1-R6-xZi" firstAttribute="leading" secondItem="fr4-mL-3Yf" secondAttribute="leading" constant="20" id="HJ4-VN-e9Y"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Yl1-R6-xZi" secondAttribute="trailing" constant="20" id="vbZ-dD-yZM"/>
|
||||
<constraint firstItem="Yl1-R6-xZi" firstAttribute="centerY" secondItem="fr4-mL-3Yf" secondAttribute="centerY" id="zsZ-z6-IFh"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="Sgf-NV-3Di">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="pTk-WJ-j5h" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="97.5" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pTk-WJ-j5h" id="ahe-yz-PGg">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mQv-3O-Y2d" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="EEL-8n-nHO"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Add Account"/>
|
||||
<connections>
|
||||
<action selector="add:" destination="vi3-jb-8XS" eventType="touchUpInside" id="lCb-LW-xZ0"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="mQv-3O-Y2d" firstAttribute="leading" secondItem="ahe-yz-PGg" secondAttribute="leading" id="7gZ-8n-bWs"/>
|
||||
<constraint firstAttribute="trailing" secondItem="mQv-3O-Y2d" secondAttribute="trailing" id="FQu-yU-a4k"/>
|
||||
<constraint firstAttribute="bottom" secondItem="mQv-3O-Y2d" secondAttribute="bottom" id="Jnx-yV-sX3"/>
|
||||
<constraint firstItem="mQv-3O-Y2d" firstAttribute="top" secondItem="ahe-yz-PGg" secondAttribute="top" id="wZS-15-Bb5"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="vi3-jb-8XS" id="U1Z-Kw-46j"/>
|
||||
<outlet property="delegate" destination="vi3-jb-8XS" id="4El-ci-jdg"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="On My Device" id="AOA-LS-PIB">
|
||||
<barButtonItem key="leftBarButtonItem" style="plain" systemItem="cancel" id="b2H-re-cgN">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="vi3-jb-8XS" id="gRE-sR-r4Z"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="nameTextField" destination="Yl1-R6-xZi" id="jcl-vI-Rde"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="XJD-sO-MSq" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2506" y="145"/>
|
||||
</scene>
|
||||
<!--Feedbin-->
|
||||
<scene sceneID="IDj-HA-olN">
|
||||
<objects>
|
||||
<tableViewController id="ECy-jg-Kyc" customClass="FeedbinAccountViewController" 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="Y0x-RC-7ln">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
||||
<sections>
|
||||
<tableViewSection id="xBN-Pb-KAy">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="lsa-Fl-Pc7">
|
||||
<rect key="frame" x="0.0" y="18" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lsa-Fl-Pc7" id="Lpd-D1-1PQ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Email" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="vJa-NN-yjR">
|
||||
<rect key="frame" x="20" y="11.5" width="374" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" spellCheckingType="no" keyboardType="emailAddress" textContentType="username"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="vJa-NN-yjR" secondAttribute="trailing" constant="20" id="7xY-Mz-Szf"/>
|
||||
<constraint firstItem="vJa-NN-yjR" firstAttribute="centerY" secondItem="Lpd-D1-1PQ" secondAttribute="centerY" id="E8M-nD-KIN"/>
|
||||
<constraint firstItem="vJa-NN-yjR" firstAttribute="leading" secondItem="Lpd-D1-1PQ" secondAttribute="leading" constant="20" id="Lgm-1L-4xL"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="Hwv-Q0-zT0">
|
||||
<rect key="frame" x="0.0" y="61.5" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Hwv-Q0-zT0" id="jIT-5L-d8d">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="YC2-RH-QoV">
|
||||
<rect key="frame" x="20" y="11.5" width="323" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" secureTextEntry="YES" textContentType="password"/>
|
||||
</textField>
|
||||
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TfW-wf-V06">
|
||||
<rect key="frame" x="351" y="5.5" width="43" height="33"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Show"/>
|
||||
<connections>
|
||||
<action selector="showHidePassword:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="QcS-lr-SPG"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="TfW-wf-V06" firstAttribute="leading" secondItem="YC2-RH-QoV" secondAttribute="trailing" constant="8" symbolic="YES" id="MHu-Ut-Kox"/>
|
||||
<constraint firstItem="TfW-wf-V06" firstAttribute="centerY" secondItem="jIT-5L-d8d" secondAttribute="centerY" id="O3Y-Jd-n9t"/>
|
||||
<constraint firstItem="YC2-RH-QoV" firstAttribute="leading" secondItem="jIT-5L-d8d" secondAttribute="leading" constant="20" id="W79-WW-Buk"/>
|
||||
<constraint firstItem="YC2-RH-QoV" firstAttribute="centerY" secondItem="jIT-5L-d8d" secondAttribute="centerY" id="iDt-ym-Qjf"/>
|
||||
<constraint firstAttribute="trailing" secondItem="TfW-wf-V06" secondAttribute="trailing" constant="20" id="rMZ-af-tPg"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="Kkf-rn-qdv">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="hWd-EN-p7e">
|
||||
<rect key="frame" x="0.0" y="141" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="hWd-EN-p7e" id="S8S-1a-vVf">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="gv7-yG-aE3" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="pt0-rn-0JI"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Action"/>
|
||||
<connections>
|
||||
<action selector="action:" destination="ECy-jg-Kyc" eventType="touchUpInside" id="79h-R2-s0C"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="gv7-yG-aE3" firstAttribute="leading" secondItem="S8S-1a-vVf" secondAttribute="leading" id="cbE-1E-Mem"/>
|
||||
<constraint firstAttribute="bottom" secondItem="gv7-yG-aE3" secondAttribute="bottom" id="luY-eY-UbU"/>
|
||||
<constraint firstItem="gv7-yG-aE3" firstAttribute="top" secondItem="S8S-1a-vVf" secondAttribute="top" id="m3N-0e-JV0"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gv7-yG-aE3" secondAttribute="trailing" id="tQC-jk-evr"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="ECy-jg-Kyc" id="hUr-Xx-9Ho"/>
|
||||
<outlet property="delegate" destination="ECy-jg-Kyc" id="DKA-Lp-mNb"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Feedbin" id="tYg-9f-kyd">
|
||||
<barButtonItem key="leftBarButtonItem" style="plain" systemItem="cancel" id="pfF-Of-5NT">
|
||||
<connections>
|
||||
<action selector="cancel:" destination="ECy-jg-Kyc" id="ZKI-gV-ylg"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" style="plain" id="Xwp-LO-qff">
|
||||
<view key="customView" contentMode="scaleToFill" id="cn4-b1-uZa">
|
||||
<rect key="frame" x="374" y="12" width="20" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="YvV-hB-lzT">
|
||||
<rect key="frame" x="36" y="6" width="20" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="actionButton" destination="gv7-yG-aE3" id="ENc-5A-hQc"/>
|
||||
<outlet property="activityIndicator" destination="YvV-hB-lzT" id="n1F-tV-5ZV"/>
|
||||
<outlet property="cancelBarButtonItem" destination="pfF-Of-5NT" id="Zr3-qD-1Yi"/>
|
||||
<outlet property="emailTextField" destination="vJa-NN-yjR" id="nCF-9W-YsF"/>
|
||||
<outlet property="passwordTextField" destination="YC2-RH-QoV" id="qaX-0i-7jq"/>
|
||||
<outlet property="showHideButton" destination="TfW-wf-V06" id="PbL-67-Nrg"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="L24-0i-kyr" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="3187" y="139"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
156
iOS/Account/FeedbinAccountViewController.swift
Normal file
156
iOS/Account/FeedbinAccountViewController.swift
Normal file
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// FeedbinAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/19/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
class FeedbinAccountViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var activityIndicator: UIActivityIndicatorView!
|
||||
@IBOutlet weak var cancelBarButtonItem: UIBarButtonItem!
|
||||
@IBOutlet weak var emailTextField: UITextField!
|
||||
@IBOutlet weak var passwordTextField: UITextField!
|
||||
@IBOutlet weak var showHideButton: UIButton!
|
||||
@IBOutlet weak var actionButton: UIButton!
|
||||
|
||||
weak var account: Account?
|
||||
weak var delegate: AddAccountDismissDelegate?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
activityIndicator.isHidden = true
|
||||
emailTextField.delegate = self
|
||||
passwordTextField.delegate = self
|
||||
|
||||
if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) {
|
||||
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
|
||||
emailTextField.text = credentials.username
|
||||
passwordTextField.text = credentials.secret
|
||||
} else {
|
||||
actionButton.setTitle(NSLocalizedString("Add Account", comment: "Update Credentials"), for: .normal)
|
||||
}
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: emailTextField)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange(_:)), name: UITextField.textDidChangeNotification, object: passwordTextField)
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
delegate?.dismiss()
|
||||
}
|
||||
|
||||
@IBAction func showHidePassword(_ sender: Any) {
|
||||
if passwordTextField.isSecureTextEntry {
|
||||
passwordTextField.isSecureTextEntry = false
|
||||
showHideButton.setTitle("Hide", for: .normal)
|
||||
} else {
|
||||
passwordTextField.isSecureTextEntry = true
|
||||
showHideButton.setTitle("Show", for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func action(_ sender: Any) {
|
||||
|
||||
guard let email = emailTextField.text, let password = passwordTextField.text else {
|
||||
showError(NSLocalizedString("Username & password required.", comment: "Credentials Error"))
|
||||
return
|
||||
}
|
||||
|
||||
startAnimatingActivityIndicator()
|
||||
disableNavigation()
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedEmail = email.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .basic, username: trimmedEmail, secret: password)
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
|
||||
|
||||
self.stopAnimtatingActivityIndicator()
|
||||
self.enableNavigation()
|
||||
|
||||
switch result {
|
||||
case .success(let credentials):
|
||||
if let credentials = credentials {
|
||||
var newAccount = false
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .feedbin)
|
||||
newAccount = true
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .basic)
|
||||
} catch {}
|
||||
try self.account?.storeCredentials(credentials)
|
||||
|
||||
if newAccount {
|
||||
self.account?.refreshAll() { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
self.presentError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
self.delegate?.dismiss()
|
||||
} catch {
|
||||
self.showError(NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error"))
|
||||
}
|
||||
} else {
|
||||
self.showError(NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error"))
|
||||
}
|
||||
case .failure:
|
||||
self.showError(NSLocalizedString("Network error. Try again later.", comment: "Credentials Error"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@objc func textDidChange(_ note: Notification) {
|
||||
actionButton.isEnabled = !(emailTextField.text?.isEmpty ?? false) && !(passwordTextField.text?.isEmpty ?? false)
|
||||
}
|
||||
|
||||
private func showError(_ message: String) {
|
||||
presentError(title: "Error", message: message)
|
||||
}
|
||||
|
||||
private func enableNavigation() {
|
||||
self.cancelBarButtonItem.isEnabled = true
|
||||
self.actionButton.isEnabled = true
|
||||
}
|
||||
|
||||
private func disableNavigation() {
|
||||
cancelBarButtonItem.isEnabled = false
|
||||
actionButton.isEnabled = false
|
||||
}
|
||||
|
||||
private func startAnimatingActivityIndicator() {
|
||||
activityIndicator.isHidden = false
|
||||
activityIndicator.startAnimating()
|
||||
}
|
||||
|
||||
private func stopAnimtatingActivityIndicator() {
|
||||
self.activityIndicator.isHidden = true
|
||||
self.activityIndicator.stopAnimating()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FeedbinAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
45
iOS/Account/LocalAccountViewController.swift
Normal file
45
iOS/Account/LocalAccountViewController.swift
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// LocalAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/19/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class LocalAccountViewController: UITableViewController {
|
||||
|
||||
@IBOutlet weak var nameTextField: UITextField!
|
||||
|
||||
weak var delegate: AddAccountDismissDelegate?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
navigationItem.title = Account.defaultLocalAccountName
|
||||
nameTextField.delegate = self
|
||||
}
|
||||
|
||||
@IBAction func cancel(_ sender: Any) {
|
||||
dismiss(animated: true, completion: nil)
|
||||
delegate?.dismiss()
|
||||
}
|
||||
|
||||
@IBAction func add(_ sender: Any) {
|
||||
let account = AccountManager.shared.createAccount(type: .onMyMac)
|
||||
account.name = nameTextField.text
|
||||
dismiss(animated: true, completion: nil)
|
||||
delegate?.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension LocalAccountViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,9 +7,26 @@
|
||||
//
|
||||
import UIKit
|
||||
import RSCore
|
||||
import Account
|
||||
|
||||
struct AppAssets {
|
||||
|
||||
static var accountLocalPadImage: UIImage = {
|
||||
return UIImage(named: "accountLocalPad")!
|
||||
}()
|
||||
|
||||
static var accountLocalPhoneImage: UIImage = {
|
||||
return UIImage(named: "accountLocalPhone")!
|
||||
}()
|
||||
|
||||
static var accountFeedbinImage: UIImage = {
|
||||
return UIImage(named: "accountFeedbin")!
|
||||
}()
|
||||
|
||||
static var accountFreshRSSImage: UIImage = {
|
||||
return UIImage(named: "accountFreshRSS")!
|
||||
}()
|
||||
|
||||
static var articleExtractorError: UIImage = {
|
||||
return UIImage(named: "articleExtractorError")!
|
||||
}()
|
||||
@@ -60,6 +77,10 @@ struct AppAssets {
|
||||
return UIImage(systemName: "doc.on.doc")!
|
||||
}()
|
||||
|
||||
static var deactivateImage: UIImage = {
|
||||
UIImage(systemName: "minus.circle")!
|
||||
}()
|
||||
|
||||
static var editImage: UIImage = {
|
||||
UIImage(systemName: "square.and.pencil")!
|
||||
}()
|
||||
@@ -140,10 +161,6 @@ struct AppAssets {
|
||||
return UIImage(systemName: "star.fill")!
|
||||
}()
|
||||
|
||||
static var tableViewCellHighlightedTextColor: UIColor = {
|
||||
return UIColor(named: "tableViewCellHighlightedTextColor")!
|
||||
}()
|
||||
|
||||
static var timelineStarImage: UIImage = {
|
||||
let image = UIImage(systemName: "star.fill")!
|
||||
return image.withTintColor(AppAssets.starColor, renderingMode: .alwaysOriginal)
|
||||
@@ -161,4 +178,25 @@ struct AppAssets {
|
||||
return UIImage(systemName: "largecircle.fill.circle")!
|
||||
}()
|
||||
|
||||
static var vibrantTextColor: UIColor = {
|
||||
return UIColor(named: "vibrantTextColor")!
|
||||
}()
|
||||
|
||||
static func image(for accountType: AccountType) -> UIImage? {
|
||||
switch accountType {
|
||||
case .onMyMac:
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
return AppAssets.accountLocalPadImage
|
||||
} else {
|
||||
return AppAssets.accountLocalPhoneImage
|
||||
}
|
||||
case .feedbin:
|
||||
return AppAssets.accountFeedbinImage
|
||||
case .freshRSS:
|
||||
return AppAssets.accountFreshRSSImage
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ struct AppDefaults {
|
||||
let defaults: [String : Any] = [Key.lastImageCacheFlushDate: Date(),
|
||||
Key.refreshInterval: RefreshInterval.everyHour.rawValue,
|
||||
Key.timelineGroupByFeed: false,
|
||||
Key.timelineNumberOfLines: 3,
|
||||
Key.timelineNumberOfLines: 2,
|
||||
Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue,
|
||||
Key.displayUndoAvailableTip: true]
|
||||
AppDefaults.shared.register(defaults: defaults)
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<!--Timeline-->
|
||||
<scene sceneID="fag-XH-avP">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="MasterTimelineViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="Kyk-vK-QRX" customClass="MasterTimelineViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableViewController storyboardIdentifier="MasterTimelineViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" clearsSelectionOnViewWillAppear="NO" id="Kyk-vK-QRX" customClass="MasterTimelineViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="mtv-Ik-FoJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
|
||||
132
iOS/Inspector/AccountInspectorViewController.swift
Normal file
132
iOS/Inspector/AccountInspectorViewController.swift
Normal file
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// AccountInspectorViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/17/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
|
||||
class AccountInspectorViewController: UITableViewController {
|
||||
|
||||
static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0)
|
||||
|
||||
@IBOutlet weak var nameTextField: UITextField!
|
||||
@IBOutlet weak var activeSwitch: UISwitch!
|
||||
|
||||
var isModal = false
|
||||
weak var account: Account?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
guard let account = account else { return }
|
||||
|
||||
nameTextField.placeholder = account.defaultName
|
||||
nameTextField.text = account.name
|
||||
nameTextField.delegate = self
|
||||
activeSwitch.isOn = account.isActive
|
||||
|
||||
navigationItem.title = account.nameForDisplay
|
||||
|
||||
if isModal {
|
||||
let doneBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(done))
|
||||
navigationItem.leftBarButtonItem = doneBarButtonItem
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
account?.name = nameTextField.text
|
||||
account?.isActive = activeSwitch.isOn
|
||||
}
|
||||
|
||||
@objc func done() {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
@IBAction func credentials(_ sender: Any) {
|
||||
guard let account = account else { return }
|
||||
switch account.type {
|
||||
case .feedbin:
|
||||
let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController
|
||||
let addViewController = navController.topViewController as! FeedbinAccountViewController
|
||||
addViewController.account = account
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
present(navController, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func deleteAccount(_ sender: Any) {
|
||||
let title = NSLocalizedString("Delete Account", comment: "Delete Account")
|
||||
let message = NSLocalizedString("Are you sure you want to delete this account? This can not be undone.", comment: "Delete Account")
|
||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
let cancelAction = UIAlertAction(title: cancelTitle, style: .cancel)
|
||||
alertController.addAction(cancelAction)
|
||||
|
||||
let markTitle = NSLocalizedString("Delete", comment: "Delete")
|
||||
let markAction = UIAlertAction(title: markTitle, style: .default) { [weak self] (action) in
|
||||
guard let account = self?.account else { return }
|
||||
AccountManager.shared.deleteAccount(account)
|
||||
self?.navigationController?.popViewController(animated: true)
|
||||
}
|
||||
alertController.addAction(markAction)
|
||||
|
||||
present(alertController, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AccountInspectorViewController {
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
guard let account = account else { return 0 }
|
||||
|
||||
if account == AccountManager.shared.defaultAccount {
|
||||
return 1
|
||||
} else if account.type == .onMyMac {
|
||||
return 2
|
||||
} else {
|
||||
return super.numberOfSections(in: tableView)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell: UITableViewCell
|
||||
|
||||
if indexPath.section == 1, let account = account, account.type == .onMyMac {
|
||||
cell = super.tableView(tableView, cellForRowAt: IndexPath(row: 0, section: 2))
|
||||
} else {
|
||||
cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
|
||||
if indexPath.section > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AccountInspectorViewController: UITextFieldDelegate {
|
||||
|
||||
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
|
||||
textField.resignFirstResponder()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
169
iOS/Inspector/Inspector.storyboard
Normal file
169
iOS/Inspector/Inspector.storyboard
Normal file
@@ -0,0 +1,169 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15508"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Account Inspector View Controller-->
|
||||
<scene sceneID="nCF-Ns-xpY">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="AccountInspectorViewController" id="1m3-fZ-n7g" customClass="AccountInspectorViewController" 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="xcc-i3-tPS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection id="vec-ab-Ylg">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="4Ue-UW-e0l" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="18" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4Ue-UW-e0l" id="9E1-ww-kYn">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="19" translatesAutoresizingMaskIntoConstraints="NO" id="mQa-0W-eVS">
|
||||
<rect key="frame" x="20" y="11.5" width="374" height="21"/>
|
||||
<subviews>
|
||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name (Optional)" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="LUW-uv-piz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="374" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="mQa-0W-eVS" firstAttribute="leading" secondItem="9E1-ww-kYn" secondAttribute="leading" constant="20" symbolic="YES" id="ATM-Pf-PSm"/>
|
||||
<constraint firstItem="mQa-0W-eVS" firstAttribute="centerY" secondItem="9E1-ww-kYn" secondAttribute="centerY" id="DWl-vJ-i3I"/>
|
||||
<constraint firstAttribute="trailing" secondItem="mQa-0W-eVS" secondAttribute="trailing" constant="20" symbolic="YES" id="aIV-cb-QTV"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="zQY-gY-BOY" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="62" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zQY-gY-BOY" id="dBp-J5-ZsY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Active" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zf0-Gm-p4F">
|
||||
<rect key="frame" x="20" y="11.5" width="48" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6YV-K0-yPS">
|
||||
<rect key="frame" x="345" y="6.5" width="51" height="31"/>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="zf0-Gm-p4F" firstAttribute="leading" secondItem="dBp-J5-ZsY" secondAttribute="leading" constant="20" symbolic="YES" id="lAQ-Ps-JwD"/>
|
||||
<constraint firstItem="zf0-Gm-p4F" firstAttribute="centerY" secondItem="dBp-J5-ZsY" secondAttribute="centerY" id="ofD-kM-lP4"/>
|
||||
<constraint firstItem="6YV-K0-yPS" firstAttribute="centerY" secondItem="dBp-J5-ZsY" secondAttribute="centerY" id="yOM-Bc-HU8"/>
|
||||
<constraint firstAttribute="trailing" secondItem="6YV-K0-yPS" secondAttribute="trailing" constant="20" id="yk4-Dh-YVh"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="9UW-s0-NPI">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="FsT-vH-rTo" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="142" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="FsT-vH-rTo" id="rJW-6J-9DM">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TYD-py-8IF" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="WmU-wG-RLQ"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Credentials"/>
|
||||
<connections>
|
||||
<action selector="credentials:" destination="1m3-fZ-n7g" eventType="touchUpInside" id="Kkh-Ag-aGX"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="TYD-py-8IF" firstAttribute="top" secondItem="rJW-6J-9DM" secondAttribute="top" id="g3l-m2-eby"/>
|
||||
<constraint firstItem="TYD-py-8IF" firstAttribute="leading" secondItem="rJW-6J-9DM" secondAttribute="leading" id="gIs-Z2-bBf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="TYD-py-8IF" secondAttribute="bottom" id="hGo-PI-2l6"/>
|
||||
<constraint firstAttribute="trailing" secondItem="TYD-py-8IF" secondAttribute="trailing" id="uFe-xv-QBo"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection id="mgY-wW-xiO">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="lgY-im-vCo" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="222" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lgY-im-vCo" id="fIH-xP-nza">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="obv-a5-Pl6" customClass="VibrantButton" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="WtN-fp-Ldt"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<state key="normal" title="Delete Account">
|
||||
<color key="titleColor" systemColor="systemRedColor" red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="deleteAccount:" destination="1m3-fZ-n7g" eventType="touchUpInside" id="gYt-Fi-Eqe"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="obv-a5-Pl6" firstAttribute="top" secondItem="fIH-xP-nza" secondAttribute="top" id="0be-8X-t2s"/>
|
||||
<constraint firstAttribute="trailing" secondItem="obv-a5-Pl6" secondAttribute="trailing" id="1Po-cv-lMZ"/>
|
||||
<constraint firstItem="obv-a5-Pl6" firstAttribute="leading" secondItem="fIH-xP-nza" secondAttribute="leading" id="4Xk-ff-eUv"/>
|
||||
<constraint firstAttribute="bottom" secondItem="obv-a5-Pl6" secondAttribute="bottom" id="SS5-Ph-SHG"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="1m3-fZ-n7g" id="eTZ-Uy-P3l"/>
|
||||
<outlet property="delegate" destination="1m3-fZ-n7g" id="ZUV-ww-yOH"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" id="Fkf-MF-Fdf"/>
|
||||
<connections>
|
||||
<outlet property="activeSwitch" destination="6YV-K0-yPS" id="d9M-GP-aTR"/>
|
||||
<outlet property="nameTextField" destination="LUW-uv-piz" id="e2P-Hq-guh"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="zcI-Zg-28D" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="864" y="-591"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="Oea-QH-lLX">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="AccountInspectorNavigationViewController" id="5wr-gz-V2t" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="3Os-JI-n4e">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="1m3-fZ-n7g" kind="relationship" relationship="rootViewController" id="N6Y-D7-T01"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Uz1-sQ-sSV" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="157" y="-591"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -15,7 +15,7 @@ protocol MasterFeedTableViewCellDelegate: class {
|
||||
func disclosureSelected(_ sender: MasterFeedTableViewCell, expanding: Bool)
|
||||
}
|
||||
|
||||
class MasterFeedTableViewCell : NNWTableViewCell {
|
||||
class MasterFeedTableViewCell : VibrantTableViewCell {
|
||||
|
||||
weak var delegate: MasterFeedTableViewCellDelegate?
|
||||
|
||||
@@ -128,23 +128,17 @@ class MasterFeedTableViewCell : NNWTableViewCell {
|
||||
|
||||
override func applyThemeProperties() {
|
||||
super.applyThemeProperties()
|
||||
titleView.highlightedTextColor = AppAssets.tableViewCellHighlightedTextColor
|
||||
titleView.highlightedTextColor = AppAssets.vibrantTextColor
|
||||
}
|
||||
|
||||
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
|
||||
super.setHighlighted(highlighted, animated: animated)
|
||||
|
||||
let tintColor = isHighlighted || isSelected ? AppAssets.tableViewCellHighlightedTextColor : AppAssets.secondaryAccentColor
|
||||
disclosureButton?.tintColor = tintColor
|
||||
faviconImageView.tintColor = tintColor
|
||||
updateVibrancy(animated: animated)
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
|
||||
let tintColor = isHighlighted || isSelected ? AppAssets.tableViewCellHighlightedTextColor : AppAssets.secondaryAccentColor
|
||||
disclosureButton?.tintColor = tintColor
|
||||
faviconImageView.tintColor = tintColor
|
||||
updateVibrancy(animated: animated)
|
||||
}
|
||||
|
||||
override func willTransition(to state: UITableViewCell.StateMask) {
|
||||
@@ -201,5 +195,14 @@ private extension MasterFeedTableViewCell {
|
||||
disclosureButton?.isHidden = !isDisclosureAvailable
|
||||
separatorInset = layout.separatorInsets
|
||||
}
|
||||
|
||||
func updateVibrancy(animated: Bool) {
|
||||
let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor
|
||||
let duration = animated ? 0.6 : 0.0
|
||||
UIView.animate(withDuration: duration) {
|
||||
self.disclosureButton?.tintColor = tintColor
|
||||
self.faviconImageView.tintColor = tintColor
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ struct MasterFeedTableViewCellLayout {
|
||||
private static let unreadCountMarginLeft = CGFloat(integerLiteral: 8)
|
||||
private static let unreadCountMarginRight = CGFloat(integerLiteral: 16)
|
||||
private static let disclosureButtonSize = CGSize(width: 44, height: 44)
|
||||
private static let verticalPadding = CGFloat(integerLiteral: 11)
|
||||
|
||||
private static let minRowHeight = CGFloat(integerLiteral: 44)
|
||||
|
||||
@@ -53,7 +54,7 @@ struct MasterFeedTableViewCellLayout {
|
||||
var rFavicon = CGRect.zero
|
||||
if !shouldShowDisclosure {
|
||||
let x = bounds.origin.x + ((MasterFeedTableViewCellLayout.disclosureButtonSize.width - MasterFeedTableViewCellLayout.imageSize.width) / 2)
|
||||
let y = UIFontMetrics.default.scaledValue(for: CGFloat(integerLiteral: 4))
|
||||
let y = UIFontMetrics.default.scaledValue(for: MasterFeedTableViewCellLayout.verticalPadding)
|
||||
rFavicon = CGRect(x: x, y: y, width: MasterFeedTableViewCellLayout.imageSize.width, height: MasterFeedTableViewCellLayout.imageSize.height)
|
||||
}
|
||||
|
||||
@@ -82,31 +83,26 @@ struct MasterFeedTableViewCellLayout {
|
||||
let labelSizeInfo = MultilineUILabelSizer.size(for: label.text ?? "", font: label.font, numberOfLines: 0, width: Int(floor(labelWidth)))
|
||||
|
||||
let rLabelx = bounds.minX + MasterFeedTableViewCellLayout.disclosureButtonSize.width
|
||||
var rLabel = CGRect(x: rLabelx, y: 0.0, width: labelSizeInfo.size.width, height: labelSizeInfo.size.height)
|
||||
let rLabely = UIFontMetrics.default.scaledValue(for: MasterFeedTableViewCellLayout.verticalPadding)
|
||||
let rLabel = CGRect(x: rLabelx, y: rLabely, width: labelSizeInfo.size.width, height: labelSizeInfo.size.height)
|
||||
|
||||
// Determine cell height
|
||||
var cellHeight = [rFavicon, rLabel, rUnread, rDisclosure].maxY()
|
||||
let paddedLabelHeight = rLabel.maxY + UIFontMetrics.default.scaledValue(for: MasterFeedTableViewCellLayout.verticalPadding)
|
||||
let maxGraphicsHeight = [rFavicon, rUnread, rDisclosure].maxY()
|
||||
var cellHeight = max(paddedLabelHeight, maxGraphicsHeight)
|
||||
if cellHeight < MasterFeedTableViewCellLayout.minRowHeight {
|
||||
cellHeight = MasterFeedTableViewCellLayout.minRowHeight
|
||||
}
|
||||
|
||||
// Center in Cell
|
||||
let newBounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.width, height: cellHeight)
|
||||
|
||||
if !shouldShowDisclosure && labelSizeInfo.numberOfLinesUsed == 1 {
|
||||
rFavicon = MasterFeedTableViewCellLayout.centerVertically(rFavicon, newBounds)
|
||||
}
|
||||
|
||||
if !unreadCountIsHidden {
|
||||
rUnread = MasterFeedTableViewCellLayout.centerVertically(rUnread, newBounds)
|
||||
}
|
||||
|
||||
if shouldShowDisclosure {
|
||||
rDisclosure = MasterFeedTableViewCellLayout.centerVertically(rDisclosure, newBounds)
|
||||
}
|
||||
|
||||
rLabel = MasterFeedTableViewCellLayout.centerVertically(rLabel, newBounds)
|
||||
|
||||
// Assign the properties
|
||||
self.height = cellHeight
|
||||
self.faviconRect = rFavicon
|
||||
|
||||
@@ -29,7 +29,7 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
|
||||
set {
|
||||
if unreadCountView.unreadCount != newValue {
|
||||
unreadCountView.unreadCount = newValue
|
||||
unreadCountView.isHidden = (newValue < 1)
|
||||
updateUnreadCountView()
|
||||
setNeedsLayout()
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
|
||||
var disclosureExpanded = false {
|
||||
didSet {
|
||||
updateDisclosureImage()
|
||||
updateUnreadCountView()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +142,14 @@ private extension MasterFeedTableViewSectionHeader {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateUnreadCountView() {
|
||||
if !disclosureExpanded && unreadCount > 0 {
|
||||
unreadCountView.isHidden = false
|
||||
} else {
|
||||
self.unreadCountView.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
func addSubviewAtInit(_ view: UIView) {
|
||||
addSubview(view)
|
||||
|
||||
@@ -11,7 +11,6 @@ import Account
|
||||
import Articles
|
||||
import RSCore
|
||||
import RSTree
|
||||
import SwiftUI
|
||||
|
||||
class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
|
||||
@@ -68,7 +67,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
navigationController?.title = NSLocalizedString("Feeds", comment: "Feeds")
|
||||
clearsSelectionOnViewWillAppear = coordinator.isRootSplitCollapsed
|
||||
applyChanges(animate: false)
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
@@ -184,6 +182,10 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
let tap = UITapGestureRecognizer(target: self, action:#selector(self.toggleSectionHeader(_:)))
|
||||
headerView.addGestureRecognizer(tap)
|
||||
|
||||
if section != 0 {
|
||||
headerView.addInteraction(UIContextMenuInteraction(delegate: self))
|
||||
}
|
||||
|
||||
return headerView
|
||||
|
||||
}
|
||||
@@ -451,7 +453,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
func restoreSelectionIfNecessary(adjustScroll: Bool) {
|
||||
if let indexPath = coordinator.masterFeedIndexPathForCurrentTimeline() {
|
||||
if adjustScroll {
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false, deselect: coordinator.isRootSplitCollapsed)
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false)
|
||||
} else {
|
||||
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
||||
}
|
||||
@@ -462,7 +464,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
if dataSource.snapshot().numberOfItems > 0 {
|
||||
if let indexPath = coordinator.currentFeedIndexPath {
|
||||
if tableView.indexPathForSelectedRow != indexPath {
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true, deselect: coordinator.isRootSplitCollapsed)
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true)
|
||||
}
|
||||
} else {
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
@@ -533,6 +535,26 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
||||
|
||||
}
|
||||
|
||||
// MARK: UIContextMenuInteractionDelegate
|
||||
|
||||
extension MasterFeedViewController: UIContextMenuInteractionDelegate {
|
||||
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
||||
|
||||
guard let sectionIndex = interaction.view?.tag,
|
||||
let sectionNode = coordinator.rootNode.childAtIndex(sectionIndex),
|
||||
let account = sectionNode.representedObject as? Account
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { suggestedActions in
|
||||
let accountInfoAction = self.getAccountInfoAction(account: account)
|
||||
let deactivateAction = self.deactivateAccountAction(account: account)
|
||||
return UIMenu(title: "", children: [accountInfoAction, deactivateAction])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: MasterTableViewCellDelegate
|
||||
|
||||
extension MasterFeedViewController: MasterFeedTableViewCellDelegate {
|
||||
@@ -848,6 +870,22 @@ private extension MasterFeedViewController {
|
||||
return action
|
||||
}
|
||||
|
||||
func getAccountInfoAction(account: Account) -> UIAction {
|
||||
let title = NSLocalizedString("Get Info", comment: "Get Info")
|
||||
let action = UIAction(title: title, image: AppAssets.infoImage) { [weak self] action in
|
||||
self?.coordinator.showAccountInspector(for: account)
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func deactivateAccountAction(account: Account) -> UIAction {
|
||||
let title = NSLocalizedString("Deactivate", comment: "Deactivate")
|
||||
let action = UIAction(title: title, image: AppAssets.deactivateImage) { action in
|
||||
account.isActive = false
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func getInfoAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? {
|
||||
guard let node = dataSource.itemIdentifier(for: indexPath), let feed = node.representedObject as? Feed else {
|
||||
return nil
|
||||
|
||||
@@ -21,7 +21,7 @@ struct MasterTimelineDefaultCellLayout: MasterTimelineCellLayout {
|
||||
static let starDimension = CGFloat(integerLiteral: 16)
|
||||
static let starSize = CGSize(width: MasterTimelineDefaultCellLayout.starDimension, height: MasterTimelineDefaultCellLayout.starDimension)
|
||||
|
||||
static let avatarSize = CGSize(width: 48.0, height: 48.0)
|
||||
static let avatarSize = CGSize(width: 32.0, height: 32.0)
|
||||
static let avatarMarginRight = CGFloat(integerLiteral: 8)
|
||||
static let avatarCornerRadius = CGFloat(integerLiteral: 4)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import UIKit
|
||||
import RSCore
|
||||
|
||||
class MasterTimelineTableViewCell: NNWTableViewCell {
|
||||
class MasterTimelineTableViewCell: VibrantTableViewCell {
|
||||
|
||||
private let titleView = MasterTimelineTableViewCell.multiLineUILabel()
|
||||
private let summaryView = MasterTimelineTableViewCell.multiLineUILabel()
|
||||
@@ -37,7 +37,7 @@ class MasterTimelineTableViewCell: NNWTableViewCell {
|
||||
override func applyThemeProperties() {
|
||||
super.applyThemeProperties()
|
||||
|
||||
let highlightedTextColor = AppAssets.tableViewCellHighlightedTextColor
|
||||
let highlightedTextColor = AppAssets.vibrantTextColor
|
||||
|
||||
titleView.highlightedTextColor = highlightedTextColor
|
||||
summaryView.highlightedTextColor = highlightedTextColor
|
||||
@@ -187,30 +187,12 @@ private extension MasterTimelineTableViewCell {
|
||||
}
|
||||
|
||||
func updateUnreadIndicator() {
|
||||
let hide = cellData.read || cellData.starred
|
||||
self.unreadIndicatorView.isHidden = hide
|
||||
self.unreadIndicatorView.frame.size = !hide ? CGSize.zero : MasterTimelineDefaultCellLayout.unreadCircleSize
|
||||
UIView.animate(
|
||||
withDuration: 0.5,
|
||||
delay: 0.0,
|
||||
usingSpringWithDamping: 0.5,
|
||||
initialSpringVelocity: 0.2,
|
||||
animations: {
|
||||
self.unreadIndicatorView.frame.size = !hide ? MasterTimelineDefaultCellLayout.unreadCircleSize : CGSize.zero
|
||||
})
|
||||
showOrHideView(unreadIndicatorView, cellData.read || cellData.starred)
|
||||
unreadIndicatorView.setNeedsDisplay()
|
||||
}
|
||||
|
||||
func updateStarView() {
|
||||
self.starView.isHidden = !self.cellData.starred
|
||||
self.starView.frame.size = self.cellData.starred ? CGSize.zero : MasterTimelineDefaultCellLayout.starSize
|
||||
UIView.animate(
|
||||
withDuration: 0.5,
|
||||
delay: 0.0,
|
||||
usingSpringWithDamping: 0.5,
|
||||
initialSpringVelocity: 0.2,
|
||||
animations: {
|
||||
self.starView.frame.size = self.cellData.starred ? MasterTimelineDefaultCellLayout.starSize : CGSize.zero
|
||||
})
|
||||
showOrHideView(starView, !cellData.starred)
|
||||
}
|
||||
|
||||
func updateAvatar() {
|
||||
@@ -251,6 +233,10 @@ private extension MasterTimelineTableViewCell {
|
||||
}
|
||||
}
|
||||
|
||||
func showOrHideView(_ view: UIView, _ shouldHide: Bool) {
|
||||
shouldHide ? hideView(view) : showView(view)
|
||||
}
|
||||
|
||||
func updateSubviews() {
|
||||
updateTitleView()
|
||||
updateSummaryView()
|
||||
|
||||
@@ -32,7 +32,7 @@ class MasterUnreadIndicatorView: UIView {
|
||||
}()
|
||||
|
||||
override func draw(_ dirtyRect: CGRect) {
|
||||
let color = isSelected ? AppAssets.tableViewCellHighlightedTextColor : AppAssets.secondaryAccentColor
|
||||
let color = isSelected ? AppAssets.vibrantTextColor : AppAssets.secondaryAccentColor
|
||||
color.setFill()
|
||||
MasterUnreadIndicatorView.bezierPath.fill()
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
clearsSelectionOnViewWillAppear = coordinator.isRootSplitCollapsed
|
||||
applyChanges(animate: false)
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
@@ -132,7 +131,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
func restoreSelectionIfNecessary(adjustScroll: Bool) {
|
||||
if let article = coordinator.currentArticle, let indexPath = dataSource.indexPath(for: article) {
|
||||
if adjustScroll {
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false, deselect: coordinator.isRootSplitCollapsed)
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false)
|
||||
} else {
|
||||
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
||||
}
|
||||
@@ -150,7 +149,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||
func updateArticleSelection(animated: Bool) {
|
||||
if let article = coordinator.currentArticle, let indexPath = dataSource.indexPath(for: article) {
|
||||
if tableView.indexPathForSelectedRow != indexPath {
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true, deselect: coordinator.isRootSplitCollapsed)
|
||||
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true)
|
||||
}
|
||||
} else {
|
||||
tableView.selectRow(at: nil, animated: animated, scrollPosition: .none)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "accountLocal.pdf"
|
||||
"filename" : "localAccountPad.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
Binary file not shown.
16
iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/Contents.json
vendored
Normal file
16
iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "localAccountPhone.pdf"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template",
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
||||
BIN
iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/localAccountPhone.pdf
vendored
Normal file
BIN
iOS/Resources/Assets.xcassets/accountLocalPhone.imageset/localAccountPhone.pdf
vendored
Normal file
Binary file not shown.
@@ -8,7 +8,6 @@
|
||||
|
||||
import UIKit
|
||||
import UserNotifications
|
||||
import SwiftUI
|
||||
import Account
|
||||
import Articles
|
||||
import RSCore
|
||||
@@ -786,9 +785,23 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
}
|
||||
|
||||
func showSettings() {
|
||||
rootSplitViewController.present(style: .formSheet) {
|
||||
SettingsView(viewModel: SettingsView.ViewModel()).environment(\.sceneCoordinator, self)
|
||||
}
|
||||
let settingsNavController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
|
||||
let settingsViewController = settingsNavController.topViewController as! SettingsViewController
|
||||
settingsNavController.modalPresentationStyle = .formSheet
|
||||
settingsNavController.preferredContentSize = SettingsViewController.preferredContentSizeForFormSheetDisplay
|
||||
settingsViewController.presentingParentController = rootSplitViewController
|
||||
rootSplitViewController.present(settingsNavController, animated: true)
|
||||
}
|
||||
|
||||
func showAccountInspector(for account: Account) {
|
||||
let accountInspectorNavController =
|
||||
UIStoryboard.inspector.instantiateViewController(identifier: "AccountInspectorNavigationViewController") as! UINavigationController
|
||||
let accountInspectorController = accountInspectorNavController.topViewController as! AccountInspectorViewController
|
||||
accountInspectorNavController.modalPresentationStyle = .formSheet
|
||||
accountInspectorNavController.preferredContentSize = AccountInspectorViewController.preferredContentSizeForFormSheetDisplay
|
||||
accountInspectorController.isModal = true
|
||||
accountInspectorController.account = account
|
||||
rootSplitViewController.present(accountInspectorNavController, animated: true)
|
||||
}
|
||||
|
||||
func showFeedInspector() {
|
||||
@@ -948,6 +961,7 @@ extension SceneCoordinator: UINavigationControllerDelegate {
|
||||
if viewController === masterTimelineViewController && !isThreePanelMode && rootSplitViewController.isCollapsed && !isArticleViewControllerPending {
|
||||
stopArticleExtractor()
|
||||
currentArticle = nil
|
||||
masterTimelineViewController?.updateArticleSelection(animated: animated)
|
||||
activityManager.invalidateReading()
|
||||
return
|
||||
}
|
||||
@@ -1696,20 +1710,3 @@ private extension SceneCoordinator {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: SwiftUI
|
||||
|
||||
struct SceneCoordinatorHolder {
|
||||
weak var value: SceneCoordinator?
|
||||
}
|
||||
|
||||
struct SceneCoordinatorKey: EnvironmentKey {
|
||||
static var defaultValue: SceneCoordinatorHolder { return SceneCoordinatorHolder(value: nil ) }
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
var sceneCoordinator: SceneCoordinator? {
|
||||
get { return self[SceneCoordinatorKey.self].value }
|
||||
set { self[SceneCoordinatorKey.self].value = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
57
iOS/Settings/AboutViewController.swift
Normal file
57
iOS/Settings/AboutViewController.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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)
|
||||
|
||||
let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 20.0, y: 0.0, width: 0.0, height: 0.0))
|
||||
buildLabel.font = UIFont.systemFont(ofSize: 11.0)
|
||||
buildLabel.textColor = UIColor.gray
|
||||
buildLabel.text = NSLocalizedString("Copyright © 2002-2019 Ranchero Software", comment: "Copyright")
|
||||
buildLabel.numberOfLines = 0
|
||||
buildLabel.sizeToFit()
|
||||
buildLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
tableView.tableFooterView = buildLabel
|
||||
|
||||
}
|
||||
|
||||
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
|
||||
textView.textColor = UIColor.label
|
||||
textView.adjustsFontForContentSizeCategory = true
|
||||
textView.font = .preferredFont(forTextStyle: .body)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// SettingsAccountLabelView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsAccountLabelView : View {
|
||||
let accountImage: String
|
||||
let accountLabel: String
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
Image(accountImage)
|
||||
.resizable()
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.frame(height: 32)
|
||||
Text(verbatim: accountLabel).font(.title)
|
||||
}
|
||||
.foregroundColor(.primary).padding(4.0)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsAccountLabelView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: "On My Device")
|
||||
.previewLayout(.fixed(width: 300, height: 44))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,52 +0,0 @@
|
||||
//
|
||||
// SettingsAddAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsAddAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State private var accountAddAction: Int? = nil
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
|
||||
NavigationLink(destination: SettingsLocalAccountView(name: ""), tag: 1, selection: $accountAddAction) {
|
||||
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName)
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAddAction = 1
|
||||
})).padding(.vertical, 16)
|
||||
|
||||
NavigationLink(destination: SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel()), tag: 2, selection: $accountAddAction) {
|
||||
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin")
|
||||
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAddAction = 2
|
||||
})).padding(.vertical, 16)
|
||||
|
||||
// NavigationLink(destination: SettingsReaderAPIAccountView(viewModel: SettingsReaderAPIAccountView.ViewModel(accountType: .freshRSS)), tag: 3, selection: $accountAddAction) {
|
||||
// SettingsAccountLabelView(accountImage: "accountFreshRSS", accountLabel: "Fresh RSS")
|
||||
// }
|
||||
// .modifier(VibrantSelectAction(action: {
|
||||
// self.accountAddAction = 3
|
||||
// }))
|
||||
|
||||
}
|
||||
.navigationBarTitle(Text("Add Account"), displayMode: .inline)
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct AddAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsAddAccountView()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,137 +0,0 @@
|
||||
//
|
||||
// SettingsDetailAccountView.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 6/13/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
struct SettingsDetailAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
@State private var credentialsAction: Int? = nil
|
||||
@State private var isDeleteAlertPresented = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section {
|
||||
HStack {
|
||||
TextField("Name", text: $viewModel.name)
|
||||
}
|
||||
Toggle(isOn: $viewModel.isActive) {
|
||||
Text("Active")
|
||||
}
|
||||
}
|
||||
if viewModel.isCreditialsAvailable {
|
||||
if viewModel.account.type == .feedbin {
|
||||
NavigationLink(destination: self.settingsFeedbinAccountView, tag: 1, selection: $credentialsAction) {
|
||||
Text("Credentials")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.credentialsAction = 1
|
||||
}))
|
||||
}
|
||||
if viewModel.account.type == .freshRSS {
|
||||
NavigationLink(destination: self.settingsReaderAPIAccountView, tag: 1, selection: $credentialsAction) {
|
||||
Text("Credentials")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.credentialsAction = 1
|
||||
}))
|
||||
}
|
||||
}
|
||||
if viewModel.isDeletable {
|
||||
Section {
|
||||
Button(action: {
|
||||
self.isDeleteAlertPresented.toggle()
|
||||
}) {
|
||||
Text("Delete Account").foregroundColor(.red)
|
||||
}
|
||||
.alert(isPresented: $isDeleteAlertPresented) {
|
||||
Alert(title: Text("Are you sure you want to delete \"\(viewModel.nameForDisplay)\"?"),
|
||||
primaryButton: Alert.Button.default(Text("Delete"), action: {
|
||||
self.viewModel.delete()
|
||||
self.presentation.wrappedValue.dismiss()
|
||||
}),
|
||||
secondaryButton: Alert.Button.cancel())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
.navigationBarTitle(Text(verbatim: viewModel.nameForDisplay), displayMode: .inline)
|
||||
|
||||
}
|
||||
|
||||
var settingsFeedbinAccountView: SettingsFeedbinAccountView {
|
||||
let feedbinViewModel = SettingsFeedbinAccountView.ViewModel(account: viewModel.account)
|
||||
return SettingsFeedbinAccountView(viewModel: feedbinViewModel)
|
||||
}
|
||||
|
||||
var settingsReaderAPIAccountView: SettingsReaderAPIAccountView {
|
||||
let readerAPIModel = SettingsReaderAPIAccountView.ViewModel(account: viewModel.account)
|
||||
return SettingsReaderAPIAccountView(viewModel: readerAPIModel)
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
let account: Account
|
||||
|
||||
init(_ account: Account) {
|
||||
self.account = account
|
||||
}
|
||||
|
||||
var nameForDisplay: String {
|
||||
account.nameForDisplay
|
||||
}
|
||||
|
||||
var name: String {
|
||||
get {
|
||||
account.name ?? ""
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
account.name = newValue.isEmpty ? nil : newValue
|
||||
}
|
||||
}
|
||||
|
||||
var isActive: Bool {
|
||||
get {
|
||||
account.isActive
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
account.isActive = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var isCreditialsAvailable: Bool {
|
||||
return account.type != .onMyMac
|
||||
}
|
||||
|
||||
var isDeletable: Bool {
|
||||
return AccountManager.shared.defaultAccount != account
|
||||
}
|
||||
|
||||
func delete() {
|
||||
AccountManager.shared.deleteAccount(account)
|
||||
ActivityManager.cleanUp(account)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsDetailAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
let viewModel = SettingsDetailAccountView.ViewModel(AccountManager.shared.defaultAccount)
|
||||
return SettingsDetailAccountView(viewModel: viewModel)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,161 +0,0 @@
|
||||
//
|
||||
// SettingsFeedbinAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
struct SettingsFeedbinAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
@State var busy: Bool = false
|
||||
@State var error: String = ""
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header:
|
||||
HStack {
|
||||
Spacer()
|
||||
SettingsAccountLabelView(accountImage: "accountFeedbin", accountLabel: "Feedbin")
|
||||
.padding()
|
||||
.layoutPriority(1.0)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
TextField("Email", text: $viewModel.email)
|
||||
.keyboardType(.emailAddress)
|
||||
.textContentType(.emailAddress)
|
||||
PasswordField(password: $viewModel.password)
|
||||
}
|
||||
Section(footer:
|
||||
HStack {
|
||||
Spacer()
|
||||
Text(verbatim: error).foregroundColor(.red)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
Button(action: { self.addAccount() }) {
|
||||
if viewModel.isUpdate {
|
||||
Text("Update Account")
|
||||
} else {
|
||||
Text("Add Account")
|
||||
}
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
.disabled(!viewModel.isValid)
|
||||
}
|
||||
}
|
||||
// .disabled(busy) // Maybe someday we can do this, but right now it crashes on the iPad
|
||||
.navigationBarTitle(Text(""), displayMode: .inline)
|
||||
}
|
||||
|
||||
private func addAccount() {
|
||||
|
||||
busy = true
|
||||
error = ""
|
||||
|
||||
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .basic, username: emailAddress, secret: viewModel.password)
|
||||
|
||||
Account.validateCredentials(type: .feedbin, credentials: credentials) { result in
|
||||
|
||||
self.busy = false
|
||||
|
||||
switch result {
|
||||
case .success(let authenticated):
|
||||
|
||||
if (authenticated != nil) {
|
||||
|
||||
var newAccount = false
|
||||
let workAccount: Account
|
||||
if self.viewModel.account == nil {
|
||||
workAccount = AccountManager.shared.createAccount(type: .feedbin)
|
||||
newAccount = true
|
||||
} else {
|
||||
workAccount = self.viewModel.account!
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try workAccount.removeCredentials(type: .basic)
|
||||
} catch {}
|
||||
try workAccount.storeCredentials(credentials)
|
||||
|
||||
if newAccount {
|
||||
workAccount.refreshAll() { result in }
|
||||
}
|
||||
|
||||
self.dismiss()
|
||||
|
||||
} catch {
|
||||
self.error = "Keychain error while storing credentials."
|
||||
}
|
||||
|
||||
} else {
|
||||
self.error = "Invalid email/password combination."
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.error = "Network error. Try again later."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presentation.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
var account: Account? = nil
|
||||
|
||||
init() {
|
||||
}
|
||||
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
if let credentials = try? account.retrieveCredentials(type: .basic) {
|
||||
self.email = credentials.username
|
||||
}
|
||||
}
|
||||
|
||||
var email: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
var password: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
var isUpdate: Bool {
|
||||
return account != nil
|
||||
}
|
||||
|
||||
var isValid: Bool {
|
||||
return !email.isEmpty && !password.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsFeedbinAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsFeedbinAccountView(viewModel: SettingsFeedbinAccountView.ViewModel())
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,59 +0,0 @@
|
||||
//
|
||||
// SettingsLocalAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsLocalAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State var name: String
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header:
|
||||
HStack {
|
||||
Spacer()
|
||||
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: Account.defaultLocalAccountName)
|
||||
.padding()
|
||||
.layoutPriority(1.0)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
HStack {
|
||||
TextField("Name", text: $name)
|
||||
}
|
||||
}
|
||||
Section {
|
||||
Button(action: { self.addAccount() }) {
|
||||
Text("Add Account")
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
}
|
||||
}
|
||||
.navigationBarTitle(Text(""), displayMode: .inline)
|
||||
}
|
||||
|
||||
private func addAccount() {
|
||||
let account = AccountManager.shared.createAccount(type: .onMyMac)
|
||||
account.name = name
|
||||
dismiss()
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presentation.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsLocalAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsLocalAccountView(name: "")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,178 +0,0 @@
|
||||
//
|
||||
// SettingsReaderAPIAccountView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Jeremy Beker on 5/28/2019.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
import RSWeb
|
||||
|
||||
struct SettingsReaderAPIAccountView : View {
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
||||
@State var busy: Bool = false
|
||||
@State var error: String = ""
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section(header:
|
||||
HStack {
|
||||
Spacer()
|
||||
SettingsAccountLabelView(accountImage: "accountFreshRSS", accountLabel: "FreshRSS")
|
||||
.padding()
|
||||
.layoutPriority(1.0)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
TextField("Email", text: $viewModel.email)
|
||||
.keyboardType(.emailAddress)
|
||||
.textContentType(.emailAddress)
|
||||
SecureField("Password", text: $viewModel.password)
|
||||
TextField("API URL:", text: $viewModel.apiURL).textContentType(.URL)
|
||||
}
|
||||
|
||||
Section(footer:
|
||||
HStack {
|
||||
Spacer()
|
||||
Text(verbatim: error).foregroundColor(.red)
|
||||
Spacer()
|
||||
}
|
||||
) {
|
||||
Button(action: { self.addAccount() }) {
|
||||
if viewModel.isUpdate {
|
||||
Text("Update Account")
|
||||
} else {
|
||||
Text("Add Account")
|
||||
}
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .center))
|
||||
.disabled(!viewModel.isValid)
|
||||
}
|
||||
}
|
||||
// .disabled(busy)
|
||||
}
|
||||
|
||||
private func addAccount() {
|
||||
|
||||
busy = true
|
||||
error = ""
|
||||
|
||||
let emailAddress = viewModel.email.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .readerBasic, username: emailAddress, secret: viewModel.password)
|
||||
guard let apiURL = URL(string: viewModel.apiURL) else {
|
||||
self.error = "Invalid API URL."
|
||||
return
|
||||
}
|
||||
|
||||
Account.validateCredentials(type: viewModel.accountType, credentials: credentials, endpoint: apiURL) { result in
|
||||
|
||||
self.busy = false
|
||||
|
||||
switch result {
|
||||
case .success(let validatedCredentials):
|
||||
|
||||
guard let validatedCredentials = validatedCredentials else {
|
||||
self.error = "Invalid email/password combination."
|
||||
return
|
||||
}
|
||||
|
||||
var newAccount = false
|
||||
let workAccount: Account
|
||||
if self.viewModel.account == nil {
|
||||
workAccount = AccountManager.shared.createAccount(type: self.viewModel.accountType)
|
||||
newAccount = true
|
||||
} else {
|
||||
workAccount = self.viewModel.account!
|
||||
}
|
||||
|
||||
do {
|
||||
|
||||
do {
|
||||
try workAccount.removeCredentials(type: .readerBasic)
|
||||
try workAccount.removeCredentials(type: .readerAPIKey)
|
||||
} catch {}
|
||||
|
||||
workAccount.endpointURL = apiURL
|
||||
|
||||
try workAccount.storeCredentials(credentials)
|
||||
try workAccount.storeCredentials(validatedCredentials)
|
||||
|
||||
if newAccount {
|
||||
workAccount.refreshAll() { result in }
|
||||
}
|
||||
|
||||
self.dismiss()
|
||||
|
||||
} catch {
|
||||
self.error = "Keychain error while storing credentials."
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.error = "Network error. Try again later."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func dismiss() {
|
||||
presentation.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
var accountType: AccountType
|
||||
var account: Account? = nil
|
||||
|
||||
init(accountType: AccountType) {
|
||||
self.accountType = accountType
|
||||
}
|
||||
|
||||
init(account: Account) {
|
||||
self.account = account
|
||||
self.accountType = account.type
|
||||
if let credentials = try? account.retrieveCredentials(type: .readerBasic) {
|
||||
self.email = credentials.username
|
||||
self.apiURL = account.endpointURL?.absoluteString ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
var email: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
var password: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
var apiURL: String = "" {
|
||||
willSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
var isUpdate: Bool {
|
||||
return account != nil
|
||||
}
|
||||
|
||||
var isValid: Bool {
|
||||
return !email.isEmpty && !password.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsReaderAPIAccountView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsReaderAPIAccountView(viewModel: SettingsReaderAPIAccountView.ViewModel(accountType: .freshRSS))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
50
iOS/Settings/AddAccountViewController.swift
Normal file
50
iOS/Settings/AddAccountViewController.swift
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// AddAccountViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 5/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Account
|
||||
import UIKit
|
||||
|
||||
protocol AddAccountDismissDelegate: UIViewController {
|
||||
func dismiss()
|
||||
}
|
||||
|
||||
class AddAccountViewController: UITableViewController, AddAccountDismissDelegate {
|
||||
|
||||
@IBOutlet private weak var localAccountImageView: UIImageView!
|
||||
@IBOutlet private weak var localAccountNameLabel: UILabel!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
localAccountImageView.image = AppAssets.image(for: .onMyMac)
|
||||
localAccountNameLabel.text = Account.defaultLocalAccountName
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "AddLocalAccountNavigationViewController") as! UINavigationController
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
let addViewController = navController.topViewController as! LocalAccountViewController
|
||||
addViewController.delegate = self
|
||||
present(navController, animated: true)
|
||||
case 1:
|
||||
let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "FeedbinAccountNavigationViewController") as! UINavigationController
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
let addViewController = navController.topViewController as! FeedbinAccountViewController
|
||||
addViewController.delegate = self
|
||||
present(navController, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func dismiss() {
|
||||
navigationController?.popViewController(animated: false)
|
||||
}
|
||||
|
||||
}
|
||||
111
iOS/Settings/RefreshIntervalViewController.swift
Normal file
111
iOS/Settings/RefreshIntervalViewController.swift
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// 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 {
|
||||
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)
|
||||
|
||||
cell.textLabel?.adjustsFontForContentSizeCategory = true
|
||||
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
689
iOS/Settings/Settings.storyboard
Normal file
689
iOS/Settings/Settings.storyboard
Normal file
@@ -0,0 +1,689 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="9cW-lu-HoC">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15508"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Settings-->
|
||||
<scene sceneID="gUB-2F-Iar">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="SettingsViewController" clearsSelectionOnViewWillAppear="NO" id="a0p-rk-skQ" customClass="SettingsViewController" 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="-1" sectionFooterHeight="18" id="Wxa-ac-xiE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection headerTitle="Notifications, Badge, Data, & More" id="Bmb-Oi-RZK">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="F9H-Kr-npj" style="IBUITableViewCellStyleDefault" id="zvg-7C-BlH" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<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="zvg-7C-BlH" id="Tqk-Tu-E6K">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Open System Settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="F9H-Kr-npj">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Accounts" id="0ac-Ze-Dh4">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="6sn-wY-hHH" style="IBUITableViewCellStyleDefault" id="XHc-rQ-7FK" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="155.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XHc-rQ-7FK" id="nmL-EM-Bsi">
|
||||
<rect key="frame" x="0.0" y="0.0" width="383" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="6sn-wY-hHH">
|
||||
<rect key="frame" x="20" y="0.0" width="355" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Timeline" id="9Pk-Y8-JVJ">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="MpA-w1-Wwh" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="255.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MpA-w1-Wwh" id="GhU-ib-Mz8">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sort Oldest to Newest" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="c9W-IF-u6i" customClass="VibrantLabel" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="169" height="22"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Keq-Np-l9O">
|
||||
<rect key="frame" x="345" 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>
|
||||
<constraint firstItem="Keq-Np-l9O" firstAttribute="centerY" secondItem="GhU-ib-Mz8" secondAttribute="centerY" id="Fn1-4L-3Mu"/>
|
||||
<constraint firstItem="c9W-IF-u6i" firstAttribute="top" secondItem="GhU-ib-Mz8" secondAttribute="topMargin" id="I6I-av-62X"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="c9W-IF-u6i" secondAttribute="bottom" id="RKN-iP-jvM"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Keq-Np-l9O" secondAttribute="trailing" constant="20" id="Ry8-Ww-bwT"/>
|
||||
<constraint firstItem="Keq-Np-l9O" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="c9W-IF-u6i" secondAttribute="trailing" constant="8" id="crw-aM-cVp"/>
|
||||
<constraint firstItem="c9W-IF-u6i" firstAttribute="leading" secondItem="GhU-ib-Mz8" secondAttribute="leadingMargin" id="hcX-Ao-zHb"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="f7r-AZ-aDn" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="299.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="f7r-AZ-aDn" id="KHC-cc-tOC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Group by Feed" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cit-i1-0Hp" customClass="VibrantLabel" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="113" height="22"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="JNi-Wz-RbU">
|
||||
<rect key="frame" x="345" y="6.5" width="51" height="31"/>
|
||||
<connections>
|
||||
<action selector="switchGroupByFeed:" destination="a0p-rk-skQ" eventType="valueChanged" id="Bxb-Jq-EEi"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="JNi-Wz-RbU" secondAttribute="trailing" constant="20" id="99y-MA-Qbl"/>
|
||||
<constraint firstItem="JNi-Wz-RbU" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="cit-i1-0Hp" secondAttribute="trailing" constant="8" id="SaK-1e-1Mg"/>
|
||||
<constraint firstItem="cit-i1-0Hp" firstAttribute="bottom" secondItem="KHC-cc-tOC" secondAttribute="bottomMargin" id="cHk-g1-Wsg"/>
|
||||
<constraint firstItem="JNi-Wz-RbU" firstAttribute="centerY" secondItem="KHC-cc-tOC" secondAttribute="centerY" id="idT-LP-oPt"/>
|
||||
<constraint firstItem="cit-i1-0Hp" firstAttribute="leading" secondItem="KHC-cc-tOC" secondAttribute="leadingMargin" id="lXq-t5-hoi"/>
|
||||
<constraint firstItem="cit-i1-0Hp" firstAttribute="top" secondItem="KHC-cc-tOC" secondAttribute="topMargin" id="ojH-3E-wea"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="5wo-fM-0l6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="343.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="5wo-fM-0l6" id="XAn-lK-LoN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Number of Text Lines:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="b2T-Uw-ugm" customClass="VibrantLabel" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="11" width="168" height="22"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minimumValue="1" maximumValue="6" translatesAutoresizingMaskIntoConstraints="NO" id="sqD-br-anp">
|
||||
<rect key="frame" x="300" y="6" width="94" height="32"/>
|
||||
<connections>
|
||||
<action selector="stepNumberOfTextLines:" destination="a0p-rk-skQ" eventType="valueChanged" id="xFW-tH-ksL"/>
|
||||
</connections>
|
||||
</stepper>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="sqD-br-anp" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="b2T-Uw-ugm" secondAttribute="trailing" constant="8" id="Fhr-Xk-5q8"/>
|
||||
<constraint firstAttribute="trailing" secondItem="sqD-br-anp" secondAttribute="trailing" constant="20" id="cEN-ac-fvx"/>
|
||||
<constraint firstItem="sqD-br-anp" firstAttribute="centerY" secondItem="XAn-lK-LoN" secondAttribute="centerY" id="cQF-53-dau"/>
|
||||
<constraint firstItem="b2T-Uw-ugm" firstAttribute="leading" secondItem="XAn-lK-LoN" secondAttribute="leadingMargin" id="dYb-iM-gqX"/>
|
||||
<constraint firstItem="b2T-Uw-ugm" firstAttribute="top" secondItem="XAn-lK-LoN" secondAttribute="topMargin" id="oDa-1a-bzG"/>
|
||||
<constraint firstItem="b2T-Uw-ugm" firstAttribute="bottom" secondItem="XAn-lK-LoN" secondAttribute="bottomMargin" id="tgj-4O-AMi"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Database" id="hAC-uA-RbS">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="qur-cL-wrM" detailTextLabel="qIl-N6-6wQ" style="IBUITableViewCellStyleValue1" id="z1J-VF-St0" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="443.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="z1J-VF-St0" id="Y8U-Ka-GeZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="383" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Refresh Interval" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="qur-cL-wrM">
|
||||
<rect key="frame" x="20" y="12" width="119.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Detail" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="qIl-N6-6wQ" customClass="VibrantLabel" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="331" y="12" width="44" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4Hg-B3-zAE" style="IBUITableViewCellStyleDefault" id="glf-Pg-s3P" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="487.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="glf-Pg-s3P" id="bPA-43-Oqh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Import OPML" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="4Hg-B3-zAE">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="25J-iX-3at" style="IBUITableViewCellStyleDefault" id="qke-Ha-PXl" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="531.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="qke-Ha-PXl" id="pZi-ck-RV5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Export OPML" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="25J-iX-3at">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="About" id="TkH-4v-yhk">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="2o6-8W-nyK" style="IBUITableViewCellStyleDefault" id="he9-Ql-yfa" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="631.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="he9-Ql-yfa" id="q6L-C8-H9a">
|
||||
<rect key="frame" x="0.0" y="0.0" width="383" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="About NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="2o6-8W-nyK">
|
||||
<rect key="frame" x="20" y="0.0" width="355" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="lOk-Dh-GfZ" style="IBUITableViewCellStyleDefault" id="GWZ-jk-qU6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="675.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="GWZ-jk-qU6" id="ZgS-bo-xDl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Website" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="lOk-Dh-GfZ">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="Pm8-6D-fdE" style="IBUITableViewCellStyleDefault" id="3cU-BG-6kK" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="719.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3cU-BG-6kK" id="Qm0-SY-0vx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="How To Support NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Pm8-6D-fdE">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="TEA-EG-V6d" style="IBUITableViewCellStyleDefault" id="4yc-ig-I61" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="763.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4yc-ig-I61" id="uQl-VP-9p9">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Github Repository" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="TEA-EG-V6d">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="Q9a-Pi-uCc" style="IBUITableViewCellStyleDefault" id="mSW-A7-8lf" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="807.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mSW-A7-8lf" id="shF-ro-Zpx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Bug Tracker" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Q9a-Pi-uCc">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="dWz-1o-EpJ" style="IBUITableViewCellStyleDefault" id="2MG-qn-idJ" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="851.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2MG-qn-idJ" id="gP9-ry-keC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Technotes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="dWz-1o-EpJ">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="dXN-Mw-yf2" style="IBUITableViewCellStyleDefault" id="F0L-Ut-reX" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="895.5" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="F0L-Ut-reX" id="5SX-M2-2jR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Add NetNewsWire News Feed" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="dXN-Mw-yf2">
|
||||
<rect key="frame" x="20" y="0.0" width="374" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="a0p-rk-skQ" id="gEP-yn-nQ6"/>
|
||||
<outlet property="delegate" destination="a0p-rk-skQ" id="upy-MC-Wpn"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="Settings" id="uf8-rU-NlH">
|
||||
<barButtonItem key="leftBarButtonItem" systemItem="done" id="HlH-s0-OwW">
|
||||
<connections>
|
||||
<action selector="done:" destination="a0p-rk-skQ" id="Mzh-25-h49"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="groupByFeedSwitch" destination="JNi-Wz-RbU" id="TwH-Kd-o6N"/>
|
||||
<outlet property="numberOfTextLinesLabel" destination="b2T-Uw-ugm" id="IUA-fR-A3U"/>
|
||||
<outlet property="numberOfTextLinesSteppper" destination="sqD-br-anp" id="naH-11-E0I"/>
|
||||
<outlet property="refreshIntervalLabel" destination="qIl-N6-6wQ" id="2gQ-Yn-FP8"/>
|
||||
<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="284" y="151"/>
|
||||
</scene>
|
||||
<!--Add Account View Controller-->
|
||||
<scene sceneID="HbE-f2-Dbd">
|
||||
<objects>
|
||||
<tableViewController storyboardIdentifier="AddAccountViewController" id="b00-4A-bV6" customClass="AddAccountViewController" 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="nw8-FO-Me5">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
|
||||
<sections>
|
||||
<tableViewSection id="m3P-em-PgI">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="55" id="UFl-6I-ucw" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="18" width="414" height="55"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="UFl-6I-ucw" id="99i-Ge-guB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="55"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="iTt-HT-Ane">
|
||||
<rect key="frame" x="20" y="11.5" width="217" height="32"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="accountLocal" translatesAutoresizingMaskIntoConstraints="NO" id="tb2-dO-AhR">
|
||||
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||
<color key="tintColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="32" id="0GF-vU-aEc"/>
|
||||
<constraint firstAttribute="width" constant="32" id="xDy-t7-26A"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="On My iPhone" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="116-rt-msI">
|
||||
<rect key="frame" x="48" y="0.0" width="169" height="32"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="iTt-HT-Ane" firstAttribute="leading" secondItem="99i-Ge-guB" secondAttribute="leading" constant="20" symbolic="YES" id="SQw-L4-v2z"/>
|
||||
<constraint firstItem="iTt-HT-Ane" firstAttribute="centerY" secondItem="99i-Ge-guB" secondAttribute="centerY" id="UaS-Yf-Q1x"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="56" id="te1-L9-osf" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="73" width="414" height="56"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="te1-L9-osf" id="DgY-u7-DRO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="56"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="7dy-NH-2zV">
|
||||
<rect key="frame" x="20" y="12" width="145" height="32"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="accountFeedbin" translatesAutoresizingMaskIntoConstraints="NO" id="wyu-mZ-3zz">
|
||||
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||
<color key="tintColor" systemColor="labelColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="32" id="LEr-1Z-N99"/>
|
||||
<constraint firstAttribute="width" constant="32" id="fZN-HH-heN"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Feedbin" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uiN-cA-Nc5">
|
||||
<rect key="frame" x="48" y="0.0" width="97" height="32"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="7dy-NH-2zV" firstAttribute="centerY" secondItem="DgY-u7-DRO" secondAttribute="centerY" id="Flf-2s-zWu"/>
|
||||
<constraint firstItem="7dy-NH-2zV" firstAttribute="leading" secondItem="DgY-u7-DRO" secondAttribute="leading" constant="20" symbolic="YES" id="H71-Jv-7uw"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
</sections>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="b00-4A-bV6" id="08h-4u-ZgK"/>
|
||||
<outlet property="delegate" destination="b00-4A-bV6" id="FKM-rN-deu"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<connections>
|
||||
<outlet property="localAccountImageView" destination="tb2-dO-AhR" id="PCa-g7-grR"/>
|
||||
<outlet property="localAccountNameLabel" destination="116-rt-msI" id="h6M-5V-392"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="kmn-Q7-rga" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="983" y="151"/>
|
||||
</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" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="55.5" width="414" height="43.5"/>
|
||||
<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="1655" 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="18" 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="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UgA-s6-Vvg">
|
||||
<rect key="frame" x="20" y="11" width="374" height="40"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle0"/>
|
||||
<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"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="UgA-s6-Vvg" secondAttribute="trailing" id="zhX-mX-1aZ"/>
|
||||
</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="80" 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="62"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" usesAttributedText="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5fQ-qz-qbW">
|
||||
<rect key="frame" x="16" y="0.0" width="382" height="62"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
|
||||
<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="198" 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="62"/>
|
||||
<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="16" y="0.0" width="382" height="62"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
|
||||
<attributedString key="attributedText"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="LiZ-Tv-tqb" firstAttribute="leading" secondItem="IPw-QQ-LYI" secondAttribute="leading" constant="16" 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" constant="16" 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="316" 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="62"/>
|
||||
<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="16" y="0.0" width="382" height="62"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
|
||||
<attributedString key="attributedText"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="YLf-rp-9nE" secondAttribute="trailing" constant="16" 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" constant="16" 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="434" 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="62"/>
|
||||
<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="16" y="0.0" width="382" height="62"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
|
||||
<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" constant="16" id="HC7-dW-NNl"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wTL-xl-1rK" secondAttribute="trailing" constant="16" 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="552" 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="62"/>
|
||||
<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="16" y="0.0" width="382" height="62"/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="tableCellGroupedBackgroundColor"/>
|
||||
<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" constant="16" id="DMB-m4-JCA"/>
|
||||
<constraint firstItem="DIp-6a-oPH" firstAttribute="leading" secondItem="6pH-5O-3V8" secondAttribute="leading" constant="16" 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="2330" y="151"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="Ezn-Ny-zye">
|
||||
<objects>
|
||||
<navigationController storyboardIdentifier="SettingsNavigationViewController" id="9cW-lu-HoC" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="rtV-Ed-zwR">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="a0p-rk-skQ" kind="relationship" relationship="rootViewController" id="kfx-yr-s91"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="YVt-ZG-okT" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-454" y="152"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="accountFeedbin" width="120" height="102"/>
|
||||
<image name="accountLocal" width="99" height="77"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -1,67 +0,0 @@
|
||||
//
|
||||
// SettingsAboutView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
struct SettingsAboutView: View {
|
||||
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
List {
|
||||
Text("NetNewsWire").font(.largeTitle)
|
||||
AttributedStringView(string: self.viewModel.about, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
Section(header: Text("CREDITS")) {
|
||||
AttributedStringView(string: self.viewModel.credits, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
Section(header: Text("ACKNOWLEDGEMENTS")) {
|
||||
AttributedStringView(string: self.viewModel.acknowledgements, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
Section(header: Text("THANKS")) {
|
||||
AttributedStringView(string: self.viewModel.thanks, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
Section(header: Text("DEDICATION"), footer: Text("Copyright © 2002-2019 Ranchero Software").font(.footnote)) {
|
||||
AttributedStringView(string: self.viewModel.dedication, preferredMaxLayoutWidth: geometry.size.width - 20)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
var about: NSAttributedString
|
||||
var credits: NSAttributedString
|
||||
var acknowledgements: NSAttributedString
|
||||
var thanks: NSAttributedString
|
||||
var dedication: NSAttributedString
|
||||
|
||||
init() {
|
||||
about = ViewModel.loadResource("About")
|
||||
credits = ViewModel.loadResource("Credits")
|
||||
acknowledgements = ViewModel.loadResource("Acknowledgments")
|
||||
thanks = ViewModel.loadResource("Thanks")
|
||||
dedication = ViewModel.loadResource("Dedication")
|
||||
}
|
||||
|
||||
private static func loadResource(_ resource: String) -> NSAttributedString {
|
||||
let url = Bundle.main.url(forResource: resource, withExtension: "rtf")!
|
||||
return try! NSAttributedString(url: url, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.rtf], documentAttributes: nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsAboutView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsAboutView(viewModel: SettingsAboutView.ViewModel())
|
||||
}
|
||||
}
|
||||
39
iOS/Settings/SettingsAccountTableViewCell.swift
Normal file
39
iOS/Settings/SettingsAccountTableViewCell.swift
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// SettingsAccountTableViewCell.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/23/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SettingsAccountTableViewCell: VibrantTableViewCell {
|
||||
|
||||
@IBOutlet weak var accountImage: UIImageView!
|
||||
@IBOutlet weak var accountNameLabel: UILabel!
|
||||
|
||||
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
|
||||
super.setHighlighted(highlighted, animated: animated)
|
||||
updateVibrancy(animated: animated)
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
super.setSelected(selected, animated: animated)
|
||||
updateVibrancy(animated: animated)
|
||||
}
|
||||
|
||||
override func applyThemeProperties() {
|
||||
super.applyThemeProperties()
|
||||
accountNameLabel?.highlightedTextColor = AppAssets.vibrantTextColor
|
||||
}
|
||||
|
||||
func updateVibrancy(animated: Bool) {
|
||||
let tintColor = isHighlighted || isSelected ? AppAssets.vibrantTextColor : UIColor.label
|
||||
let duration = animated ? 0.6 : 0.0
|
||||
UIView.animate(withDuration: duration) {
|
||||
self.accountImage?.tintColor = tintColor
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
47
iOS/Settings/SettingsAccountTableViewCell.xib
Normal file
47
iOS/Settings/SettingsAccountTableViewCell.xib
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15508"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="JCb-QB-CrO" customClass="SettingsAccountTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JCb-QB-CrO" id="FzD-t2-JGy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="383" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="yiw-9t-gil">
|
||||
<rect key="frame" x="20" y="11" width="22" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="22" id="43E-Em-Z6O"/>
|
||||
<constraint firstAttribute="height" constant="22" id="mTY-cQ-1R1"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TRx-RV-za8">
|
||||
<rect key="frame" x="50" y="11.5" width="42" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="TRx-RV-za8" firstAttribute="centerY" secondItem="FzD-t2-JGy" secondAttribute="centerY" id="95H-d4-DVW"/>
|
||||
<constraint firstItem="TRx-RV-za8" firstAttribute="leading" secondItem="yiw-9t-gil" secondAttribute="trailing" constant="8" symbolic="YES" id="RUN-Ol-xSl"/>
|
||||
<constraint firstItem="yiw-9t-gil" firstAttribute="leading" secondItem="FzD-t2-JGy" secondAttribute="leading" constant="20" symbolic="YES" id="oU9-E3-lEt"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="TRx-RV-za8" secondAttribute="trailing" constant="8" id="sJ6-wr-JIw"/>
|
||||
<constraint firstItem="yiw-9t-gil" firstAttribute="centerY" secondItem="FzD-t2-JGy" secondAttribute="centerY" id="tUD-tI-dgr"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="accountImage" destination="yiw-9t-gil" id="E8w-FW-Jc9"/>
|
||||
<outlet property="accountNameLabel" destination="TRx-RV-za8" id="BOl-hK-2mT"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="7" y="-9"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,35 +0,0 @@
|
||||
//
|
||||
// SettingsRefreshSelectionView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsRefreshSelectionView: View {
|
||||
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@Binding var selectedInterval: RefreshInterval
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
ForEach(RefreshInterval.allCases) { interval in
|
||||
Button(action: {
|
||||
self.selectedInterval = interval
|
||||
self.presentation.wrappedValue.dismiss()
|
||||
}) {
|
||||
HStack {
|
||||
Text(interval.description())
|
||||
Spacer()
|
||||
if interval == self.selectedInterval {
|
||||
Image(systemName: "checkmark")
|
||||
}
|
||||
}
|
||||
}.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// SettingsSubscriptionsExportAccountPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/20/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsExportAccountPickerView: View {
|
||||
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State private var selectedAccount: Account?
|
||||
@State private var isOPMLExportDocPickerPresented: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
ForEach(AccountManager.shared.sortedAccounts) { account in
|
||||
Button(action: {
|
||||
self.selectedAccount = account
|
||||
self.isOPMLExportDocPickerPresented = true
|
||||
}) {
|
||||
Text(verbatim: account.nameForDisplay)
|
||||
}.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
}
|
||||
}.sheet(isPresented: $isOPMLExportDocPickerPresented, onDismiss: { self.presentation.wrappedValue.dismiss() }) {
|
||||
SettingsSubscriptionsExportDocumentPickerView(account: self.selectedAccount!)
|
||||
}
|
||||
.navigationBarTitle(Text("Select Account"), displayMode: .inline)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
//
|
||||
// SettingsSubscriptionsExportDocumentPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsExportDocumentPickerView : UIViewControllerRepresentable {
|
||||
var account: Account
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<SettingsSubscriptionsExportDocumentPickerView>) -> UIDocumentPickerViewController {
|
||||
|
||||
let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces)
|
||||
let filename = "Subscriptions-\(accountName).opml"
|
||||
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
|
||||
|
||||
let opmlString = OPMLExporter.OPMLString(with: account, title: filename)
|
||||
try? opmlString.write(to: tempFile, atomically: true, encoding: String.Encoding.utf8)
|
||||
|
||||
return UIDocumentPickerViewController(url: tempFile, in: .exportToService)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<SettingsSubscriptionsExportDocumentPickerView>) {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// SettingsSubscriptionsImportAccountPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/20/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsImportAccountPickerView: View {
|
||||
|
||||
@Environment(\.presentationMode) var presentation
|
||||
@State private var selectedAccount: Account?
|
||||
@State private var isOPMLImportDocPickerPresented: Bool = false
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
ForEach(AccountManager.shared.sortedActiveAccounts) { account in
|
||||
Button(action: {
|
||||
self.selectedAccount = account
|
||||
self.isOPMLImportDocPickerPresented = true
|
||||
}) {
|
||||
Text(verbatim: account.nameForDisplay)
|
||||
}.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
}
|
||||
}.sheet(isPresented: $isOPMLImportDocPickerPresented, onDismiss: { self.presentation.wrappedValue.dismiss() }) {
|
||||
SettingsSubscriptionsImportDocumentPickerView(account: self.selectedAccount!)
|
||||
}
|
||||
.navigationBarTitle(Text("Select Account"), displayMode: .inline)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
//
|
||||
// SettingsSubscriptionsImportDocumentPickerView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
struct SettingsSubscriptionsImportDocumentPickerView : UIViewControllerRepresentable {
|
||||
var account: Account
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<SettingsSubscriptionsImportDocumentPickerView>) -> UIDocumentPickerViewController {
|
||||
let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import)
|
||||
docPicker.delegate = context.coordinator
|
||||
return docPicker
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIDocumentPickerViewController, context: UIViewControllerRepresentableContext<SettingsSubscriptionsImportDocumentPickerView>) {
|
||||
//
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(self)
|
||||
}
|
||||
|
||||
class Coordinator : NSObject, UIDocumentPickerDelegate {
|
||||
var parent: SettingsSubscriptionsImportDocumentPickerView
|
||||
|
||||
init(_ view: SettingsSubscriptionsImportDocumentPickerView) {
|
||||
self.parent = view
|
||||
}
|
||||
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
for url in urls {
|
||||
parent.account.importOPML(url) { result in}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
21
iOS/Settings/SettingsTableViewCell.xib
Normal file
21
iOS/Settings/SettingsTableViewCell.xib
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15508"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="JCb-QB-CrO" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="JCb-QB-CrO" id="FzD-t2-JGy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="383" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
<point key="canvasLocation" x="7" y="-9"/>
|
||||
</tableViewCell>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,270 +0,0 @@
|
||||
//
|
||||
// SettingsView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 6/11/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import Account
|
||||
|
||||
struct SettingsView : View {
|
||||
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
|
||||
@Environment(\.viewController) private var viewController: UIViewController?
|
||||
@Environment(\.sceneCoordinator) private var coordinator: SceneCoordinator?
|
||||
|
||||
@State private var accountAction: Int? = nil
|
||||
@State private var refreshAction: Int? = nil
|
||||
@State private var importOPMLAction: Int? = nil
|
||||
@State private var exportOPMLAction: Int? = nil
|
||||
@State private var aboutAction: Int? = nil
|
||||
|
||||
@State private var isWebsitePresented: Bool = false
|
||||
@State private var website: String? = nil
|
||||
|
||||
@State private var isOPMLImportPresented: Bool = false
|
||||
@State private var isOPMLImportDocPickerPresented: Bool = false
|
||||
@State private var isOPMLExportPresented: Bool = false
|
||||
@State private var isOPMLExportDocPickerPresented: Bool = false
|
||||
@State private var opmlAccount: Account? = nil
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
Form {
|
||||
buildAccountsSection()
|
||||
buildTimelineSection()
|
||||
buildDatabaseSection()
|
||||
buildAboutSection()
|
||||
}
|
||||
.buttonStyle(VibrantButtonStyle(alignment: .leading))
|
||||
.navigationBarTitle(Text("Settings"), displayMode: .inline)
|
||||
.navigationBarItems(leading: Button(action: { self.viewController?.dismiss(animated: true) }) { Text("Done") } )
|
||||
}
|
||||
}
|
||||
|
||||
func buildAccountsSection() -> some View {
|
||||
Section(header: Text("ACCOUNTS").padding(.top, 22.0)) {
|
||||
ForEach(viewModel.accounts.indices, id: \.self) { index in
|
||||
NavigationLink(destination: SettingsDetailAccountView(viewModel: SettingsDetailAccountView.ViewModel(self.viewModel.accounts[index])), tag: index, selection: self.$accountAction) {
|
||||
Text(verbatim: self.viewModel.accounts[index].nameForDisplay)
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAction = index
|
||||
}))
|
||||
}
|
||||
NavigationLink(destination: SettingsAddAccountView(), tag: 1000, selection: $accountAction) {
|
||||
Text("Add Account")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.accountAction = 1000
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func buildTimelineSection() -> some View {
|
||||
Section(header: Text("TIMELINE")) {
|
||||
Toggle(isOn: $viewModel.sortOldestToNewest) {
|
||||
Text("Sort Newest to Oldest")
|
||||
}
|
||||
Toggle(isOn: $viewModel.groupByFeed) {
|
||||
Text("Group By Feed")
|
||||
}
|
||||
Stepper(value: $viewModel.timelineNumberOfLines, in: 2...6) {
|
||||
Text("Number of Text Lines: \(viewModel.timelineNumberOfLines)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func buildDatabaseSection() -> some View {
|
||||
Section(header: Text("DATABASE")) {
|
||||
|
||||
NavigationLink(destination: SettingsRefreshSelectionView(selectedInterval: $viewModel.refreshInterval), tag: 1, selection: $refreshAction) {
|
||||
HStack {
|
||||
Text("Refresh Interval")
|
||||
Spacer()
|
||||
Text(verbatim: self.viewModel.refreshInterval.description()).foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.refreshAction = 1
|
||||
}))
|
||||
|
||||
NavigationLink(destination: SettingsSubscriptionsImportAccountPickerView(), tag: 1, selection: $importOPMLAction) {
|
||||
Text("Import Subscriptions")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.importOPMLAction = 1
|
||||
}))
|
||||
|
||||
NavigationLink(destination: SettingsSubscriptionsExportAccountPickerView(), tag: 1, selection: $exportOPMLAction) {
|
||||
Text("Export Subscriptions")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.exportOPMLAction = 1
|
||||
}))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func buildAboutSection() -> some View {
|
||||
Section(header: Text("ABOUT"), footer: buildFooter()) {
|
||||
|
||||
NavigationLink(destination: SettingsAboutView(viewModel: SettingsAboutView.ViewModel()), tag: 1, selection: $aboutAction) {
|
||||
Text("About NetNewsWire")
|
||||
}
|
||||
.modifier(VibrantSelectAction(action: {
|
||||
self.aboutAction = 1
|
||||
}))
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://ranchero.com/netnewswire/"
|
||||
}) {
|
||||
Text("Website")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire"
|
||||
}) {
|
||||
Text("Github Repository")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire/issues"
|
||||
}) {
|
||||
Text("Bug Tracker")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes"
|
||||
}) {
|
||||
Text("Technotes")
|
||||
}
|
||||
|
||||
Button(action: {
|
||||
self.isWebsitePresented.toggle()
|
||||
self.website = "https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown"
|
||||
}) {
|
||||
Text("How To Support NetNewsWire")
|
||||
}
|
||||
|
||||
if !AccountManager.shared.anyAccountHasFeedWithURL("https://nnw.ranchero.com/feed.json") {
|
||||
Button(action: {
|
||||
self.viewController?.dismiss(animated: true) {
|
||||
let feedName = NSLocalizedString("NetNewsWire News", comment: "NetNewsWire News")
|
||||
self.coordinator?.showAdd(.feed, initialFeed: "https://nnw.ranchero.com/feed.json", initialFeedName: feedName)
|
||||
}
|
||||
}) {
|
||||
Text("Add NetNewsWire News Feed")
|
||||
}
|
||||
}
|
||||
|
||||
}.sheet(isPresented: $isWebsitePresented) {
|
||||
SafariView(url: URL(string: self.website!)!)
|
||||
}
|
||||
}
|
||||
|
||||
func buildFooter() -> some View {
|
||||
return Text(verbatim: "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
// MARK: ViewModel
|
||||
|
||||
class ViewModel: ObservableObject {
|
||||
|
||||
let objectWillChange = ObservableObjectPublisher()
|
||||
|
||||
init() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidAddAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidDeleteAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
||||
}
|
||||
|
||||
var accounts: [Account] {
|
||||
get {
|
||||
return AccountManager.shared.sortedAccounts
|
||||
}
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
var activeAccounts: [Account] {
|
||||
get {
|
||||
return AccountManager.shared.sortedActiveAccounts
|
||||
}
|
||||
set {
|
||||
}
|
||||
}
|
||||
|
||||
var sortOldestToNewest: Bool {
|
||||
get {
|
||||
return AppDefaults.timelineSortDirection == .orderedDescending
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
if newValue == true {
|
||||
AppDefaults.timelineSortDirection = .orderedDescending
|
||||
} else {
|
||||
AppDefaults.timelineSortDirection = .orderedAscending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var groupByFeed: Bool {
|
||||
get {
|
||||
return AppDefaults.timelineGroupByFeed
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
AppDefaults.timelineGroupByFeed = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var timelineNumberOfLines: Int {
|
||||
get {
|
||||
return AppDefaults.timelineNumberOfLines
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
AppDefaults.timelineNumberOfLines = newValue
|
||||
}
|
||||
}
|
||||
|
||||
var refreshInterval: RefreshInterval {
|
||||
get {
|
||||
return AppDefaults.refreshInterval
|
||||
}
|
||||
set {
|
||||
objectWillChange.send()
|
||||
AppDefaults.refreshInterval = newValue
|
||||
}
|
||||
}
|
||||
|
||||
@objc func accountsDidChange(_ notification: Notification) {
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
@objc func displayNameDidChange(_ notification: Notification) {
|
||||
objectWillChange.send()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
struct SettingsView_Previews : PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsView(viewModel: SettingsView.ViewModel())
|
||||
}
|
||||
}
|
||||
#endif
|
||||
393
iOS/Settings/SettingsViewController.swift
Normal file
393
iOS/Settings/SettingsViewController.swift
Normal file
@@ -0,0 +1,393 @@
|
||||
//
|
||||
// SettingsViewController.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 4/24/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Account
|
||||
import SafariServices
|
||||
|
||||
class SettingsViewController: UITableViewController {
|
||||
|
||||
private let appNewsURLString = "https://nnw.ranchero.com/feed.json"
|
||||
private weak var opmlAccount: Account?
|
||||
|
||||
static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0)
|
||||
|
||||
@IBOutlet weak var refreshIntervalLabel: UILabel!
|
||||
@IBOutlet weak var timelineSortOrderSwitch: UISwitch!
|
||||
@IBOutlet weak var groupByFeedSwitch: UISwitch!
|
||||
@IBOutlet weak var numberOfTextLinesLabel: UILabel!
|
||||
@IBOutlet weak var numberOfTextLinesSteppper: UIStepper!
|
||||
|
||||
weak var presentingParentController: UIViewController?
|
||||
|
||||
override func viewDidLoad() {
|
||||
// This hack mostly works around a bug in static tables with dynamic type. See: https://spin.atomicobject.com/2018/10/15/dynamic-type-static-uitableview/
|
||||
NotificationCenter.default.removeObserver(tableView!, name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidAddAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil)
|
||||
|
||||
tableView.register(UINib(nibName: "SettingsAccountTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsAccountTableViewCell")
|
||||
tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell")
|
||||
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if AppDefaults.timelineSortDirection == .orderedAscending {
|
||||
timelineSortOrderSwitch.isOn = true
|
||||
} else {
|
||||
timelineSortOrderSwitch.isOn = false
|
||||
}
|
||||
|
||||
if AppDefaults.timelineGroupByFeed {
|
||||
groupByFeedSwitch.isOn = true
|
||||
} else {
|
||||
groupByFeedSwitch.isOn = false
|
||||
}
|
||||
|
||||
refreshIntervalLabel.text = AppDefaults.refreshInterval.description()
|
||||
|
||||
let numberOfTextLines = AppDefaults.timelineNumberOfLines
|
||||
numberOfTextLinesSteppper.value = Double(numberOfTextLines)
|
||||
updateNumberOfTextLinesLabel(value: numberOfTextLines)
|
||||
|
||||
let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 20.0, y: 0.0, width: 0.0, height: 0.0))
|
||||
buildLabel.font = UIFont.systemFont(ofSize: 11.0)
|
||||
buildLabel.textColor = UIColor.gray
|
||||
buildLabel.text = "\(Bundle.main.appName) v \(Bundle.main.versionNumber) (Build \(Bundle.main.buildNumber))"
|
||||
buildLabel.sizeToFit()
|
||||
buildLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
tableView.tableFooterView = buildLabel
|
||||
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
self.tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
}
|
||||
|
||||
// MARK: UITableView
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
switch section {
|
||||
case 1:
|
||||
return AccountManager.shared.accounts.count + 1
|
||||
case 4:
|
||||
let defaultNumberOfRows = super.tableView(tableView, numberOfRowsInSection: section)
|
||||
if AccountManager.shared.activeAccounts.isEmpty || AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) {
|
||||
return defaultNumberOfRows - 1
|
||||
}
|
||||
return defaultNumberOfRows
|
||||
default:
|
||||
return super.tableView(tableView, numberOfRowsInSection: section)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell: UITableViewCell
|
||||
switch indexPath.section {
|
||||
case 1:
|
||||
|
||||
let sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
if indexPath.row == sortedAccounts.count {
|
||||
cell = tableView.dequeueReusableCell(withIdentifier: "SettingsTableViewCell", for: indexPath)
|
||||
cell.textLabel?.adjustsFontForContentSizeCategory = true
|
||||
cell.textLabel?.text = NSLocalizedString("Add Account", comment: "Accounts")
|
||||
} else {
|
||||
let acctCell = tableView.dequeueReusableCell(withIdentifier: "SettingsAccountTableViewCell", for: indexPath) as! SettingsAccountTableViewCell
|
||||
acctCell.applyThemeProperties()
|
||||
let account = sortedAccounts[indexPath.row]
|
||||
acctCell.accountImage?.image = AppAssets.image(for: account.type)
|
||||
acctCell.accountNameLabel?.text = account.nameForDisplay
|
||||
cell = acctCell
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
|
||||
}
|
||||
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
UIApplication.shared.open(URL(string: "\(UIApplication.openSettingsURLString)")!)
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 1:
|
||||
let sortedAccounts = AccountManager.shared.sortedAccounts
|
||||
if indexPath.row == sortedAccounts.count {
|
||||
let controller = UIStoryboard.settings.instantiateController(ofType: AddAccountViewController.self)
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
} else {
|
||||
let controller = UIStoryboard.inspector.instantiateController(ofType: AccountInspectorViewController.self)
|
||||
controller.account = sortedAccounts[indexPath.row]
|
||||
self.navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
case 3:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let timeline = UIStoryboard.settings.instantiateController(ofType: RefreshIntervalViewController.self)
|
||||
self.navigationController?.pushViewController(timeline, animated: true)
|
||||
case 1:
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
if let sourceView = tableView.cellForRow(at: indexPath) {
|
||||
let sourceRect = tableView.rectForRow(at: indexPath)
|
||||
importOPML(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
case 2:
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
if let sourceView = tableView.cellForRow(at: indexPath) {
|
||||
let sourceRect = tableView.rectForRow(at: indexPath)
|
||||
exportOPML(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
case 4:
|
||||
switch indexPath.row {
|
||||
case 0:
|
||||
let timeline = UIStoryboard.settings.instantiateController(ofType: AboutViewController.self)
|
||||
self.navigationController?.pushViewController(timeline, animated: true)
|
||||
case 1:
|
||||
openURL("https://ranchero.com/netnewswire/")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 2:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/HowToSupportNetNewsWire.markdown")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 3:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 4:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire/issues")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 5:
|
||||
openURL("https://github.com/brentsimmons/NetNewsWire/tree/master/Technotes")
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
case 6:
|
||||
addFeed()
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
tableView.selectRow(at: nil, animated: true, scrollPosition: .none)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
|
||||
return .none
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
if indexPath.section == 1 {
|
||||
return super.tableView(tableView, heightForRowAt: IndexPath(row: 0, section: 1))
|
||||
} else {
|
||||
return super.tableView(tableView, heightForRowAt: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, indentationLevelForRowAt indexPath: IndexPath) -> Int {
|
||||
if indexPath.section == 1 {
|
||||
return super.tableView(tableView, indentationLevelForRowAt: IndexPath(row: 0, section: 1))
|
||||
} else {
|
||||
return super.tableView(tableView, indentationLevelForRowAt: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
@IBAction func done(_ sender: Any) {
|
||||
dismiss(animated: true)
|
||||
}
|
||||
|
||||
@IBAction func switchTimelineOrder(_ sender: Any) {
|
||||
if timelineSortOrderSwitch.isOn {
|
||||
AppDefaults.timelineSortDirection = .orderedAscending
|
||||
} else {
|
||||
AppDefaults.timelineSortDirection = .orderedDescending
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func switchGroupByFeed(_ sender: Any) {
|
||||
if groupByFeedSwitch.isOn {
|
||||
AppDefaults.timelineGroupByFeed = true
|
||||
} else {
|
||||
AppDefaults.timelineGroupByFeed = false
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func stepNumberOfTextLines(_ sender: UIStepper) {
|
||||
let numberOfLines = Int(sender.value)
|
||||
AppDefaults.timelineNumberOfLines = numberOfLines
|
||||
updateNumberOfTextLinesLabel(value: numberOfLines)
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc func contentSizeCategoryDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func accountsDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
@objc func displayNameDidChange() {
|
||||
tableView.reloadData()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: OPML Document Picker
|
||||
|
||||
extension SettingsViewController: UIDocumentPickerDelegate {
|
||||
|
||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
||||
for url in urls {
|
||||
opmlAccount?.importOPML(url) { result in}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension SettingsViewController {
|
||||
|
||||
func updateNumberOfTextLinesLabel(value: Int) {
|
||||
let localizedText = NSLocalizedString("Number of Text Lines: %d", comment: "Number of Text Lines")
|
||||
numberOfTextLinesLabel.text = NSString.localizedStringWithFormat(localizedText as NSString, value) as String
|
||||
}
|
||||
|
||||
func addFeed() {
|
||||
self.dismiss(animated: true)
|
||||
|
||||
let addNavViewController = UIStoryboard.add.instantiateInitialViewController() as! UINavigationController
|
||||
let addViewController = addNavViewController.topViewController as! AddContainerViewController
|
||||
addNavViewController.modalPresentationStyle = .formSheet
|
||||
addNavViewController.preferredContentSize = AddContainerViewController.preferredContentSizeForFormSheetDisplay
|
||||
addViewController.initialControllerType = .feed
|
||||
addViewController.initialFeed = appNewsURLString
|
||||
addViewController.initialFeedName = "NetNewsWire News"
|
||||
|
||||
presentingParentController?.present(addNavViewController, animated: true)
|
||||
}
|
||||
|
||||
func importOPML(sourceView: UIView, sourceRect: CGRect) {
|
||||
switch AccountManager.shared.activeAccounts.count {
|
||||
case 0:
|
||||
presentError(title: "Error", message: NSLocalizedString("You must have at least one active account.", comment: "Missing active account"))
|
||||
case 1:
|
||||
opmlAccount = AccountManager.shared.activeAccounts.first
|
||||
importOPMLDocumentPicker()
|
||||
default:
|
||||
importOPMLAccountPicker(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
}
|
||||
|
||||
func importOPMLAccountPicker(sourceView: UIView, sourceRect: CGRect) {
|
||||
let title = NSLocalizedString("Select an Import Account", comment: "Select an Import Account")
|
||||
let alert = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
if let popoverController = alert.popoverPresentationController {
|
||||
popoverController.sourceView = view
|
||||
popoverController.sourceRect = sourceRect
|
||||
}
|
||||
|
||||
for account in AccountManager.shared.sortedActiveAccounts {
|
||||
let action = UIAlertAction(title: account.nameForDisplay, style: .default) { [weak self] action in
|
||||
self?.opmlAccount = account
|
||||
self?.importOPMLDocumentPicker()
|
||||
}
|
||||
alert.addAction(action)
|
||||
}
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
alert.addAction(UIAlertAction(title: cancelTitle, style: .cancel))
|
||||
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
|
||||
func importOPMLDocumentPicker() {
|
||||
let docPicker = UIDocumentPickerViewController(documentTypes: ["public.xml", "org.opml.opml"], in: .import)
|
||||
docPicker.delegate = self
|
||||
docPicker.modalPresentationStyle = .formSheet
|
||||
self.present(docPicker, animated: true)
|
||||
}
|
||||
|
||||
func exportOPML(sourceView: UIView, sourceRect: CGRect) {
|
||||
if AccountManager.shared.accounts.count == 1 {
|
||||
exportOPMLDocumentPicker()
|
||||
} else {
|
||||
exportOPMLAccountPicker(sourceView: sourceView, sourceRect: sourceRect)
|
||||
}
|
||||
}
|
||||
|
||||
func exportOPMLAccountPicker(sourceView: UIView, sourceRect: CGRect) {
|
||||
let title = NSLocalizedString("Select an Export Account", comment: "Select an Export Account")
|
||||
let alert = UIAlertController(title: title, message: nil, preferredStyle: .actionSheet)
|
||||
|
||||
if let popoverController = alert.popoverPresentationController {
|
||||
popoverController.sourceView = view
|
||||
popoverController.sourceRect = sourceRect
|
||||
}
|
||||
|
||||
for account in AccountManager.shared.sortedAccounts {
|
||||
let action = UIAlertAction(title: account.nameForDisplay, style: .default) { [weak self] action in
|
||||
self?.opmlAccount = account
|
||||
self?.exportOPMLDocumentPicker()
|
||||
}
|
||||
alert.addAction(action)
|
||||
}
|
||||
|
||||
let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel")
|
||||
alert.addAction(UIAlertAction(title: cancelTitle, style: .cancel))
|
||||
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
|
||||
func exportOPMLDocumentPicker() {
|
||||
guard let account = opmlAccount else { return }
|
||||
|
||||
let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces)
|
||||
let filename = "Subscriptions-\(accountName).opml"
|
||||
let tempFile = FileManager.default.temporaryDirectory.appendingPathComponent(filename)
|
||||
let opmlString = OPMLExporter.OPMLString(with: account, 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)
|
||||
}
|
||||
|
||||
func openURL(_ urlString: String) {
|
||||
let vc = SFSafariViewController(url: URL(string: urlString)!)
|
||||
vc.modalPresentationStyle = .pageSheet
|
||||
present(vc, animated: true)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
//
|
||||
// AttributedStringView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct AttributedStringView: UIViewRepresentable {
|
||||
|
||||
let string: NSAttributedString
|
||||
let preferredMaxLayoutWidth: CGFloat
|
||||
|
||||
func makeUIView(context: Context) -> HackedTextView {
|
||||
return HackedTextView()
|
||||
}
|
||||
|
||||
func updateUIView(_ view: HackedTextView, context: Context) {
|
||||
view.attributedText = string
|
||||
|
||||
view.preferredMaxLayoutWidth = preferredMaxLayoutWidth
|
||||
view.isScrollEnabled = false
|
||||
view.textContainer.lineBreakMode = .byWordWrapping
|
||||
|
||||
view.isUserInteractionEnabled = true
|
||||
view.adjustsFontForContentSizeCategory = true
|
||||
view.font = .preferredFont(forTextStyle: .body)
|
||||
view.textColor = UIColor.label
|
||||
view.tintColor = AppAssets.secondaryAccentColor
|
||||
view.backgroundColor = UIColor.secondarySystemGroupedBackground
|
||||
|
||||
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||
view.setContentCompressionResistancePriority(.required, for: .vertical)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class HackedTextView: UITextView {
|
||||
var preferredMaxLayoutWidth = CGFloat.zero
|
||||
override var intrinsicContentSize: CGSize {
|
||||
return sizeThatFits(CGSize(width: preferredMaxLayoutWidth, height: .infinity))
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// PasswordField.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/8/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct PasswordField: UIViewRepresentable {
|
||||
|
||||
let password: Binding<String>
|
||||
|
||||
func makeUIView(context: Context) -> ShowHidePasswordView {
|
||||
let showHideView = Bundle.main.loadNibNamed("ShowHidePasswordView", owner: Self.self, options: nil)?[0] as! ShowHidePasswordView
|
||||
showHideView.passwordTextField.bindingString = password
|
||||
return showHideView
|
||||
}
|
||||
|
||||
func updateUIView(_ showHideView: ShowHidePasswordView, context: Context) {
|
||||
showHideView.passwordTextField.bindingString = password
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
//
|
||||
// SafariView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Stuart Breckenridge on 16/6/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SafariServices
|
||||
|
||||
struct SafariView : UIViewControllerRepresentable {
|
||||
|
||||
let url: URL
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<SafariView>) -> SFSafariViewController {
|
||||
let safari = SFSafariViewController(url: url)
|
||||
safari.delegate = context.coordinator
|
||||
return safari
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SafariView>) {
|
||||
//
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
return Coordinator(self)
|
||||
}
|
||||
|
||||
class Coordinator : NSObject, SFSafariViewControllerDelegate {
|
||||
var parent: SafariView
|
||||
|
||||
init(_ safariView: SafariView) {
|
||||
self.parent = safariView
|
||||
}
|
||||
|
||||
// MARK: SFSafariViewControllerDelegate
|
||||
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
|
||||
|
||||
}
|
||||
|
||||
func safariViewController(_ controller: SFSafariViewController, initialLoadDidRedirectTo URL: URL) {
|
||||
|
||||
}
|
||||
|
||||
func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
//
|
||||
// ShowHidePasswordView.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/8/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
class ShowHidePasswordView: UIView {
|
||||
|
||||
@IBOutlet weak var passwordTextField: BindingTextField!
|
||||
@IBOutlet weak var showHideButton: UIButton!
|
||||
|
||||
@IBAction func toggleShowHideButton(_ sender: Any) {
|
||||
if passwordTextField.isSecureTextEntry {
|
||||
passwordTextField.isSecureTextEntry = false
|
||||
showHideButton.setTitle(NSLocalizedString("Hide", comment: "Hide"), for: .normal)
|
||||
} else {
|
||||
passwordTextField.isSecureTextEntry = true
|
||||
showHideButton.setTitle(NSLocalizedString("Show", comment: "Show"), for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BindingTextField: UITextField, UITextFieldDelegate {
|
||||
|
||||
var bindingString: Binding<String>? = nil
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
delegate = self
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
delegate = self
|
||||
}
|
||||
|
||||
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
|
||||
if let currentValue = textField.text as NSString? {
|
||||
let proposedValue = currentValue.replacingCharacters(in: range, with: string)
|
||||
bindingString?.wrappedValue = proposedValue
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15504" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15508"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ShowHidePasswordView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="284" height="54"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TT2-5T-DdA">
|
||||
<rect key="frame" x="246" y="12" width="38" height="30"/>
|
||||
<state key="normal" title="Show"/>
|
||||
<connections>
|
||||
<action selector="toggleShowHideButton:" destination="iN0-l3-epB" eventType="touchUpInside" id="DTB-2f-JoB"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="249" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Password" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Iwe-LN-7ZI" customClass="BindingTextField" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="16.5" width="238" height="21"/>
|
||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||
<textInputTraits key="textInputTraits" secureTextEntry="YES"/>
|
||||
</textField>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="TT2-5T-DdA" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="Buq-YS-fG8"/>
|
||||
<constraint firstItem="TT2-5T-DdA" firstAttribute="leading" secondItem="Iwe-LN-7ZI" secondAttribute="trailing" constant="8" symbolic="YES" id="FIM-1x-LoT"/>
|
||||
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="TT2-5T-DdA" secondAttribute="trailing" id="b4w-k0-zUR"/>
|
||||
<constraint firstItem="Iwe-LN-7ZI" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="eGg-IF-dp6"/>
|
||||
<constraint firstItem="Iwe-LN-7ZI" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="mUI-zV-GHb"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
<connections>
|
||||
<outlet property="passwordTextField" destination="Iwe-LN-7ZI" id="Tvk-Q4-kHr"/>
|
||||
<outlet property="showHideButton" destination="TT2-5T-DdA" id="1GH-1O-ma0"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="43.478260869565219" y="-127.23214285714285"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// VibrantButtonStyle.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/16/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct VibrantButtonStyle: ButtonStyle {
|
||||
|
||||
let alignment: Alignment
|
||||
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
GeometryReader { geometry in
|
||||
configuration.label
|
||||
.frame(width: geometry.size.width, height: geometry.size.height, alignment: self.alignment)
|
||||
}
|
||||
.foregroundColor(configuration.isPressed ? Color(AppAssets.tableViewCellHighlightedTextColor) : .primary)
|
||||
.listRowBackground(configuration.isPressed ? Color(AppAssets.primaryAccentColor) : Color(.secondarySystemGroupedBackground))
|
||||
.background(configuration.isPressed ? Color(AppAssets.primaryAccentColor) : Color(.secondarySystemGroupedBackground))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// VibrantSelectAction.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/15/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct VibrantSelectAction: ViewModifier {
|
||||
|
||||
let action: () -> Void
|
||||
@State var isTapped = false
|
||||
@GestureState var isLongPressed = false
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
GeometryReader { geometry in
|
||||
content
|
||||
.frame(width: geometry.size.width, height: geometry.size.height, alignment: .leading)
|
||||
.background(self.isLongPressed || self.isTapped ? Color(AppAssets.primaryAccentColor) : Color(.secondarySystemGroupedBackground))
|
||||
}
|
||||
.foregroundColor(isLongPressed || isTapped ? Color(AppAssets.tableViewCellHighlightedTextColor) : .primary)
|
||||
.listRowBackground(isLongPressed || isTapped ? Color(AppAssets.primaryAccentColor) : nil)
|
||||
.gesture(
|
||||
LongPressGesture().onEnded( { _ in self.action() })
|
||||
.updating($isLongPressed) { value, state, transcation in state = value }
|
||||
.simultaneously(with:
|
||||
TapGesture().onEnded( {
|
||||
self.isTapped = true
|
||||
self.action()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
|
||||
self.isTapped = false
|
||||
}
|
||||
}
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,10 +34,10 @@ class ThemedNavigationController: UINavigationController {
|
||||
|
||||
if traitCollection.userInterfaceStyle == .dark {
|
||||
navigationBar.standardAppearance = UINavigationBarAppearance()
|
||||
navigationBar.tintColor = view.tintColor
|
||||
navigationBar.tintColor = AppAssets.primaryAccentColor
|
||||
toolbar.standardAppearance = UIToolbarAppearance()
|
||||
toolbar.compactAppearance = UIToolbarAppearance()
|
||||
toolbar.tintColor = view.tintColor
|
||||
toolbar.tintColor = AppAssets.primaryAccentColor
|
||||
} else {
|
||||
let navigationAppearance = UINavigationBarAppearance()
|
||||
navigationAppearance.backgroundColor = AppAssets.barBackgroundColor
|
||||
|
||||
@@ -10,6 +10,8 @@ import UIKit
|
||||
|
||||
extension UIStoryboard {
|
||||
|
||||
static let preferredContentSizeForFormSheetDisplay = CGSize(width: 460.0, height: 400.0)
|
||||
|
||||
static var main: UIStoryboard {
|
||||
return UIStoryboard(name: "Main", bundle: nil)
|
||||
}
|
||||
@@ -22,6 +24,14 @@ extension UIStoryboard {
|
||||
return UIStoryboard(name: "Settings", bundle: nil)
|
||||
}
|
||||
|
||||
static var inspector: UIStoryboard {
|
||||
return UIStoryboard(name: "Inspector", bundle: nil)
|
||||
}
|
||||
|
||||
static var account: UIStoryboard {
|
||||
return UIStoryboard(name: "Account", bundle: nil)
|
||||
}
|
||||
|
||||
func instantiateController<T>(ofType type: T.Type = T.self) -> T where T: UIViewController {
|
||||
|
||||
let storyboardId = String(describing: type)
|
||||
|
||||
48
iOS/UIKit Extensions/VibrantButton.swift
Normal file
48
iOS/UIKit Extensions/VibrantButton.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// VibrantButton.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/22/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class VibrantButton: UIButton {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
setTitleColor(AppAssets.vibrantTextColor, for: .highlighted)
|
||||
}
|
||||
|
||||
override var isHighlighted: Bool {
|
||||
didSet {
|
||||
backgroundColor = isHighlighted ? AppAssets.secondaryAccentColor : nil
|
||||
titleLabel?.alpha = 1
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
isHighlighted = true
|
||||
super.touchesBegan(touches, with: event)
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
isHighlighted = false
|
||||
super.touchesEnded(touches, with: event)
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
isHighlighted = false
|
||||
super.touchesCancelled(touches, with: event)
|
||||
}
|
||||
|
||||
}
|
||||
27
iOS/UIKit Extensions/VibrantLabel.swift
Normal file
27
iOS/UIKit Extensions/VibrantLabel.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// VibrantLabel.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 10/22/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class VibrantLabel: UILabel {
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
highlightedTextColor = AppAssets.vibrantTextColor
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// NNWTableViewCell.swift
|
||||
// VibrantTableViewCell.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Jim Correia on 9/2/19.
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class NNWTableViewCell: UITableViewCell {
|
||||
class VibrantTableViewCell: UITableViewCell {
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
commonInit()
|
||||
@@ -26,7 +27,9 @@ class NNWTableViewCell: UITableViewCell {
|
||||
/// Subclass overrides should call super
|
||||
func applyThemeProperties() {
|
||||
let selectedBackgroundView = UIView(frame: .zero)
|
||||
selectedBackgroundView.backgroundColor = AppAssets.primaryAccentColor
|
||||
selectedBackgroundView.backgroundColor = AppAssets.secondaryAccentColor
|
||||
self.selectedBackgroundView = selectedBackgroundView
|
||||
|
||||
textLabel?.highlightedTextColor = AppAssets.vibrantTextColor
|
||||
}
|
||||
}
|
||||
Submodule submodules/RSCore updated: 29dc34284b...fa16a5b1a0
Submodule submodules/Sparkle updated: d5222353e6...67819be18a
@@ -1,3 +1,4 @@
|
||||
#include "./NetNewsWire_project_debug.xcconfig"
|
||||
|
||||
OTHER_SWIFT_FLAGS = -DTEST $(inherited)
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) $(SYMROOT)/Release$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
CODE_SIGN_IDENTITY[config=Release] = 3rd Party Mac Developer Application
|
||||
CODE_SIGN_IDENTITY[config=Debug] = -
|
||||
DEVELOPMENT_TEAM = M8L2WTLA8W
|
||||
CODE_SIGN_STYLE = Manual
|
||||
ORGANIZATION_IDENTIFIER = com.ranchero
|
||||
PROVISIONING_PROFILE_SPECIFIER =
|
||||
|
||||
// developers can locally override the Xcode settings for code signing
|
||||
// by creating a DeveloperSettings.xcconfig file locally at the appropriate path
|
||||
// This allows a pristine project to have code signing set up with the appropriate
|
||||
// developer ID and certificates, and for dev to be able to have local settings
|
||||
// without needing to check in anything into source control
|
||||
//
|
||||
// As an example, make a ../../SharedXcodeSettings/DeveloperSettings.xcconfig file and
|
||||
// give it the contents
|
||||
//
|
||||
// CODE_SIGN_IDENTITY[sdk=macosx*] = Mac Developer
|
||||
// CODE_SIGN_IDENTITY[sdk=iphoneos*] = iPhone Developer
|
||||
// CODE_SIGN_IDENTITY[sdk=iphonesimulator*] = iPhone Developer
|
||||
// DEVELOPMENT_TEAM = <Your Team ID>
|
||||
// ORGANIZATION_IDENTIFIER = <Your Domain Name Reversed>
|
||||
// CODE_SIGN_STYLE = Automatic
|
||||
// PROVISIONING_PROFILE_SPECIFIER =
|
||||
//
|
||||
// And you should be able to build without code signing errors and without modifying
|
||||
// the NetNewsWire Xcode project.
|
||||
//
|
||||
// Example: if your NetNewsWire Xcode project file is at
|
||||
// /Users/Shared/git/NetNewsWire/NetNewsWire.xcodeproj
|
||||
// create your DeveloperSettings.xcconfig file at
|
||||
// /Users/Shared/git/SharedXcodeSettings/DeveloperSettings.xcconfig
|
||||
//
|
||||
|
||||
#include? "../../SharedXcodeSettings/DeveloperSettings.xcconfig"
|
||||
#include "./common/NetNewsWire_mac_target_common.xcconfig"
|
||||
|
||||
CODE_SIGN_ENTITLEMENTS = Mac/SafariExtension/Subscribe_to_Feed.entitlements
|
||||
INFOPLIST_FILE = Mac/SafariExtension/Info.plist
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @executable_path/../../../../Frameworks
|
||||
PRODUCT_BUNDLE_IDENTIFIER = $(ORGANIZATION_IDENTIFIER).NetNewsWire-Evergreen.MAS.Subscribe-to-Feed
|
||||
PRODUCT_NAME = $(TARGET_NAME)
|
||||
OTHER_SWIFT_FLAGS = -DMAC_APP_STORE $(inherited)
|
||||
|
||||
SDKROOT = macosx
|
||||
Reference in New Issue
Block a user