Merge pull request #1 from brentsimmons/master

merge from upstream
This commit is contained in:
Olof Hellman
2019-08-11 20:53:52 -07:00
committed by GitHub
39 changed files with 666 additions and 241 deletions

View File

@@ -444,7 +444,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
@IBAction func showHelp(_ sender: Any?) {
Browser.open("https://ranchero.com/netnewswire/help/5.0/en/", inBackground: false)
Browser.open("https://ranchero.com/netnewswire/help/mac/5.0/en/", inBackground: false)
}
@IBAction func donateToAppCampForGirls(_ sender: Any?) {

View File

@@ -66,7 +66,7 @@
</class>
<enumeration name="account type" code="enum">
<enumerator name="local" code="Locl" description="The On my Mac account"/>
<enumerator name="onmymac" code="Locl" description="An On my Mac (local) account"/>
<enumerator name="feedly" code="Fdly" description="A Feedly account"/>
<enumerator name="feedbin" code="Fdbn" description="A Feedbin account"/>
<enumerator name="feed wrangler" code="FWrg" description="A Feed Wrangler account"/>
@@ -77,20 +77,20 @@
<class name="account" code="Acct" plural="accounts" description="An account for subscribing to feeds">
<cocoa class="ScriptableAccount"/>
<property name="name" code="pnam" type="text" access="r" description="The name of the account">
<cocoa key="name"/>
<cocoa key="scriptingName"/>
</property>
<property name="id" code="ID " type="text" access="r" description="The unique id of the account">
<cocoa key="uniqueId"/>
</property>
<property name="type" code="ATyp" type="account type" access="r" description="The type of the account">
<property name="accountType" code="ATyp" type="account type" access="r" description="The type of the account">
<cocoa key="accountType"/>
</property>
<property name="active" code="Actv" type="boolean" access="rw" description="Whether or not the account is active">
<cocoa key="scriptingIsActive"/>
</property>
<property name="contents" code="Cnts" access="r" description="The type of the account">
<cocoa key="contents"/>
<type type="account item" list="yes"/>
<property name="allFeeds" code="Feds" access="r" description="All feeds, including feeds inside folders">
<cocoa key="allFeeds"/>
<type type="feed" list="yes"/>
</property>
<property name="opml representation" code="OPML" type="text" access="r" description="OPML representation for the account">
<cocoa key="opmlRepresentation"/>
@@ -103,10 +103,7 @@
</element>
</class>
<class name="account item" code="AItm" plural="account items" description="a folder or feed">
</class>
<class name="feed" code="Feed" plural="feeds" description="An RSS feeds" inherits="account item">
<class name="feed" code="Feed" plural="feeds" description="An RSS feeds">
<cocoa class="ScriptableFeed"/>
<property name="name" code="pnam" type="text" access="r" description="The name of the feed">
<cocoa key="name"/>
@@ -156,7 +153,7 @@
</property>
</class>
<class name="folder" code="fold" plural="folders" description="A folder for organizing feeds" inherits="account item">
<class name="folder" code="fold" plural="folders" description="A folder for organizing feeds">
<cocoa class="ScriptableFolder"/>
<property name="name" code="pnam" type="text" access="r" description="The name of the account">
<cocoa key="name"/>

View File

@@ -35,7 +35,17 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
account.isActive = newValue
}
}
@objc(scriptingName)
var scriptingName: NSString {
get {
return account.nameForDisplay as NSString
}
set {
account.name = newValue as String
}
}
// MARK: --- ScriptingObject protocol ---
var scriptingKey: String {
@@ -121,18 +131,21 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
// MARK: --- Scriptable properties ---
@objc(contents)
var contents:NSArray {
var contentsArray:[AnyObject] = []
@objc(allFeeds)
var allFeeds: NSArray {
var feeds = [ScriptableFeed]()
for feed in account.topLevelFeeds {
contentsArray.append(ScriptableFeed(feed, container: self))
feeds.append(ScriptableFeed(feed, container: self))
}
if let folders = account.folders {
for folder in folders {
contentsArray.append(ScriptableFolder(folder, container:self))
let scriptableFolder = ScriptableFolder(folder, container: self)
for feed in folder.topLevelFeeds {
feeds.append(ScriptableFeed(feed, container: scriptableFolder))
}
}
}
return contentsArray as NSArray
return feeds as NSArray
}
@objc(opmlRepresentation)

View File

@@ -89,17 +89,17 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
guard let url = self.urlForNewFeed(arguments:arguments) else {return nil}
if let existingFeed = account.existingFeed(withURL:url) {
return self.scriptableFeed(existingFeed, account:account, folder:folder)
return scriptableFeed(existingFeed, account:account, folder:folder).objectSpecifier
}
let container: Container = folder != nil ? folder! : account
// at this point, we need to download the feed and parse it.
// RS Parser does the callback for the download on the main thread (which it probably shouldn't?)
// because we can't wait here (on the main thread, maybe) for the callback, we have to return from this function
// Generally, returning from an AppleEvent handler function means that handling the appleEvent is over,
// but we don't yet have the result of the event yet, so we prevent the AppleEvent from returning by calling
// suspendExecution(). When we get the callback, we can supply the event result and call resumeExecution()
// We need to download the feed and parse it.
// RSParser does the callback for the download on the main thread.
// Because we can't wait here (on the main thread) for the callback, we have to return from this function.
// Generally, returning from an AppleEvent handler function means that handling the Apple event is over,
// but we dont yet have the result of the event yet, so we prevent the Apple event from returning by calling
// suspendExecution(). When we get the callback, we supply the event result and call resumeExecution().
command.suspendExecution()
account.createFeed(url: url, name: titleFromArgs, container: container) { result in

View File

@@ -56,7 +56,7 @@ extension NSApplication : ScriptingObjectContainer {
@objc(accounts)
func accounts() -> NSArray {
let accounts = AccountManager.shared.activeAccounts
let accounts = AccountManager.shared.accounts
return accounts.map { ScriptableAccount($0) } as NSArray
}

View File

@@ -2383,7 +2383,6 @@
51C452852265093600C03939 /* AddFeedFolderPickerData.swift in Sources */,
51C4526B226508F600C03939 /* MasterFeedViewController.swift in Sources */,
5126EE97226CB48A00C22AFC /* AppCoordinator.swift in Sources */,
5126EE97226CB48A00C22AFC /* AppCoordinator.swift in Sources */,
84CAFCB022BC8C35007694F0 /* FetchRequestOperation.swift in Sources */,
51EF0F77227716200050506E /* FaviconGenerator.swift in Sources */,
51C4525A226508D600C03939 /* UIStoryboard-Extensions.swift in Sources */,

View File

@@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "849C645F1ED37A5D003D8FC0"
BuildableName = ".app"
BuildableName = "NetNewsWire.app"
BlueprintName = "NetNewsWire"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
@@ -31,7 +31,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "849C645F1ED37A5D003D8FC0"
BuildableName = ".app"
BuildableName = "NetNewsWire.app"
BlueprintName = "NetNewsWire"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
@@ -69,7 +69,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "849C645F1ED37A5D003D8FC0"
BuildableName = ".app"
BuildableName = "NetNewsWire.app"
BlueprintName = "NetNewsWire"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>
@@ -86,7 +86,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "849C645F1ED37A5D003D8FC0"
BuildableName = ".app"
BuildableName = "NetNewsWire.app"
BlueprintName = "NetNewsWire"
ReferencedContainer = "container:NetNewsWire.xcodeproj">
</BuildableReference>

View File

@@ -65,7 +65,6 @@ struct TimelineStringFormatter {
s = s.replacingOccurrences(of: "\r", with: "")
s = s.replacingOccurrences(of: "\t", with: "")
s = s.rsparser_stringByDecodingHTMLEntities()
s = s.replacingOccurrences(of: "", with: "")
s = s.rs_stringByTrimmingWhitespace()
s = s.rs_stringWithCollapsedWhitespace()

View File

@@ -2,7 +2,7 @@
The Help book goes here. Text files go at the top level, and images go in the images directory.
Text files are markdown files. A script ([wildcat](https://github.com/brentsimmons/wildcat)) will turn this into a web site that will appear at [https://ranchero.com/netnewswire/help/5.0/en/](https://ranchero.com/netnewswire/help/5.0/en/). (The /en/ directory because we hope to have localized versions.)
Text files are markdown files. A script ([wildcat](https://github.com/brentsimmons/wildcat)) will turn this into a web site that will appear at [https://ranchero.com/netnewswire/help/mac/5.0/en/](https://ranchero.com/netnewswire/help/mac/5.0/en/). (The /en/ directory because we hope to have localized versions.)
If youd like to write or edit the Help book, or help with screenshots, please bring it up on the [NetNewsWire Slack](https://netnewswire.slack.com/join/shared_invite/enQtNjM4MDA1MjQzMDkzLTNlNjBhOWVhYzdhYjA4ZWFhMzQ1MTUxYjU0NTE5ZGY0YzYwZWJhNjYwNTNmNTg2NjIwYWY4YzhlYzk5NmU3ZTc) group. Well coordinate there.

View File

@@ -15,33 +15,37 @@ Before you finish, you can choose an alternative name for the feed and where it
Click Add and NetNewsWire will fetch the URL you entered. If you entered an address thats not a feed, NetNewsWire will search the page and add the feed it finds.
## Choosing an alternative name for a feed
Choosing an alternative name for a feed
---------------------------------------
Feeds specify their own name, but you may want to change it to something easier to remember. Brent rather than inessential, for example. You also change this later in the [feed inspector].
## Choosing a feeds folder
Choosing a feeds folder
------------------------
Before you add a feed, you can choose the account and folder where it will be saved.
This option is especially important if youre using multiple accounts. You can choose whether to save the subscription to your [On My Mac](on-my-mac.html) account or [Feedbin] account.
This option is especially important if youre using multiple accounts. You can choose whether to save the subscription to your [On My Mac](on-my-mac.html) account or [Feedbin](syncing-accounts.html) account.
In either case, if you use folders, you can also choose which one keep the feed in.
## What to do when NetNewsWire cant find a feed
Sometimes NetNewsWire wont be able to find a feed for a site. Either the site doesnt offer a feed, or the feed isnt advertised in a way that NetNewsWire can find it.
What to do when NetNewsWire cant find a feed
---------------------------------------------
Sometimes NetNewsWire wont be able to find a feed for a site. Either the site doesnt offer a feed, or the feed isnt advertised in a way that NetNewsWire can find it.
You may be able to find a feed manually by visiting the site. There, look for a link to an RSS, Atom or JSON feed. If one exists, you can add this direct URL to NetNewsWire using the process above. Right-click on the link and copy the URL to paste into NetNewsWire.
## Other ways to add feeds
### Safari Extension
Other ways to add feeds
-----------------------
You can also add a feed from Safari using the [NetNewsWire Safari Extension](safari-extension.html).
Adding feeds in the NetNewsWire app isnt the only way. You can also add feeds from Safari using the [NetNewsWire Safari Extension](safari-extension.html).
### Importing an OPML list of feeds
If you have an existing subscription list in OPML format, you can [import those feeds into NetNewsWire](import-opml.html).
If you have an existing OPML subscription list from another app or service, you can [import those feeds into NetNewsWire](import-opml.html), too.

View File

@@ -6,10 +6,16 @@ NetNewsWire is open-source and contributions from the community are welcomed.
There are two channels for contributing. There is a Slack group for conducting discussions. For filing tickets, and contributing code or documentation, we use GitHub.
## Slack
Slack
-----
[Join the Slack group](https://join.slack.com/t/netnewswire/shared_invite/enQtNjM4MDA1MjQzMDkzLTNlNjBhOWVhYzdhYjA4ZWFhMzQ1MTUxYjU0NTE5ZGY0YzYwZWJhNjYwNTNmNTg2NjIwYWY4YzhlYzk5NmU3ZTc) to talk with other NetNewsWire users — and to help out, if youd like to, by testing, coding, writing, providing feedback, or just helping us think things through. Everybody is welcome and encouraged to join.
## GitHub
GitHub
------
The [NetNewsWire project on GitHub](https://github.com/brentsimmons/NetNewsWire) is the place to raise formal issues like bugs and feature requests. If you want to make contributions to NetNewsWires code or documentation, please refer to our [contribution guidelines](https://github.com/brentsimmons/NetNewsWire/blob/master/CONTRIBUTING.md) page on GitHub.

View File

@@ -4,6 +4,9 @@
To delete a feed or folder, select it from the subscription list and choose **Edit → Delete** from the menu bar. Alternatively, select the feed and press the **delete** key.
## Undo deleting a feed or folder
Undo deleting a feed or folder
------------------------------
If you change your mind, you can undo the deletion by selecting **Edit → Undo** in the menu bar or pressing ⌘-Z.

View File

@@ -1,52 +1,77 @@
@title NetNewsWire 5 Help
## Getting Started
[What is RSS? What are feeds?](what-is-rss.html)
More topics to do…
## Reading Feeds
[How to go through your articles](reading-articles.html)
[How to sort the timeline](sorting-the-timeline.html)
Tour of the built-in smart feeds: Today, All Unread, Starred
[Keyboard shortcuts](keyboard-shortcuts.html)
Sharing to other apps, including MarsEdit and Micro.blog
## Managing Feeds
[How to add a feed](adding-feeds.html)
[Installing and using the Safari Extension to add feeds](safari-extension.html)
[How to import OPML](import-opml.html)
[How to export OPML](export-opml.html)
[How to go through your articles](reading-articles.html)
Sharing to other apps, including MarsEdit and Micro.blog
[How to delete feeds and folders](deleting-feeds-folders.html)
How to add a syncing account
[How to contribute](contributing.html)
[How to get NetNewsWire news](netnewswire-news.html)
Keyboard shortcuts (see Help menu command)
[About the On My Mac account](on-my-mac.html)
Privacy (including link to privacy policy)
How to add special Micro.blog feeds
How to rename feeds and folders
How to get and copy feed information
Where NetNewsWire data is stored
How to update NetNewsWire
How to sort the timeline
## Accounts
Accounts overview
[About the On My Mac account](on-my-mac.html)
[How to add a syncing account](syncing-accounts.html)
## NetNewsWire Data
[Where NetNewsWire data is stored](userdata-location.html)
[How to import OPML](import-opml.html)
[How to export OPML](export-opml.html)
## The NetNewsWire Project
[How to Update NetNewsWire](updating.html)
[How to get NetNewsWire news](netnewswire-news.html)
[NetNewsWire and your privacy](privacy.html)
[How to contribute to NetNewsWire](contributing.html)
## Other ideas? Add them here.
Tour of the built-in smart feeds: Today, All Unread, Starred
Other ideas? Add them here.
---
Credits: The Help book was written [Brent Simmons](https://inessential.com/), your name here, etc.
Credits: The Help book was written by [Brent Simmons](https://inessential.com/), [Ryan Dotson](https://nostodnayr.net), your name here, etc.

View File

@@ -0,0 +1,55 @@
@title Keyboard shortcuts
# Keyboard shortcuts
NetNewsWires interface is easily navigable using keyboard shortcuts.
The most useful shortcuts are for reading articles: **space** to scroll articles and jump to the next unread one, or **n** to jump immediately to the next unread article. Theres also **b** to open the article in your browser (the **Return** or **Enter** keys work too).
In-app list of keyboard shortcuts
---------------------------------
A comprehensive list of NetNewsWires keyboard shortcuts is always accessible to you in NetNewsWire. Just click on **Help → Keyboard Shortcuts** in the menu bar.
The list opens in a separate window which you can leave open to remind you of the keys to press. *Youll be a NetNewsWire keyboard magician in no time.*
### Do I need to use the Command key?
No, the Command (⌘) key isnt required for these advanced keyboard shortcuts  just press the key by itself.
NetNewsWires keyboard shortcuts
--------------------------------
Many of NetNewsWires shortcuts work from anywhere in the app. Others only work when a certain pane is active and highlighted.
<!-- From in-app shortcut page -->
<table>
<tr><th>Everywhere in NetNewsWire</th></tr>
<tr><td>Scroll or go to next unread</td> <td>space</td></tr>
<tr><td>Go to next unread</td> <td>n or +</td></tr>
<tr><td>Mark as read</td> <td>r</td></tr>
<tr><td>Mark all as read</td> <td>k</td></tr>
<tr><td>Mark older articles as read</td> <td>o</td></tr>
<tr><td>Mark all as read, go to next unread</td> <td>l</td></tr>
<tr><td>Mark as unread, go to next unread</td> <td>m</td></tr>
<tr><td>Mark as unread</td> <td>u</td></tr>
<tr><td>Open in browser</td> <td>b or &#9166; or Enter</td></tr>
<tr><td>Previous subscription</td> <td>a</td></tr>
<tr><td>Next subscription</td> <td>z</td></tr>
<tr><th>Feed list (left pane)</th></tr>
<tr><td>Collapse</td> <td>, or &#8997;+&larr;</td></tr>
<tr><td>Expand</td> <td>. or &#8997;+&rarr;</td></tr>
<tr><td>Collapse All (except for group items)</td> <td>; or &#8997;+&#8984;+&larr;</td></tr>
<tr><td>Expand All</td> <td>' or &#8997;+&#8984;+&rarr;</td></tr>
<tr><td>Move focus to headlines</td> <td>&rarr;</td></tr>
<tr><th>Article list timeline (centre pane)</th></tr>
<tr><td>Move focus to subscriptions</td> <td>&larr;</td></tr>
<tr><td>Move focus to detail</td> <td>&rarr;</td></tr>
<tr><th>Article reader (right pane)</th></tr>
<tr><td>Move focus to headlines</td> <td>&larr;</td></tr>
</table>

View File

@@ -4,7 +4,10 @@
The very best way to get news about NetNewsWire is with NetNewsWire itself, using the *NetNewsWire News Feed*. Important and notable news about NetNewsWire will appear there: new version announcements, critical bug notices, tech notes and important project announcements.
## Add the NetNewsWire News Feed
Add the NetNewsWire News Feed
-----------------------------
The NetNewsWire News Feed is part of the default subscription list installed the first time you launched NetNewsWire. If its no longer in your list, its easy to add it back.

View File

@@ -1,6 +1,6 @@
@title About the On My Mac account
@title The On My Mac account
# About the On My Mac account
# The On My Mac account
The On My Mac account is the simplest way to use NetNewsWire. Using it requires no additional service or software. Its just you, your subscriptions and NetNewsWire.
@@ -8,7 +8,33 @@ On My Mac subscriptions are wholly managed by NetNewsWire. It keeps your subscri
The On My Mac account does not sync this data to any other location. It works best for those people who only read NetNewsWire feeds on one Mac and nowhere else.
*NetNewsWire can also be used with a [syncing account](syncing-accounts.html) service like Feedbin.*
## Refreshing On My Mac feeds
The feeds in the On My Mac account will be refreshed automatically whenever you open NetNewsWire. If left open, NetNewsWire will refresh your feeds every hour, or according to the schedule you set in Preferences. You can also disable all automatic refreshing from there.
Refreshing On My Mac feeds
--------------------------
The feeds in the On My Mac account will be refreshed automatically whenever you open NetNewsWire. If left open, NetNewsWire will refresh your feeds every hour, or according to the schedule you set in Preferences.
If you dont use the On My Mac account
--------------------------------------
If you only use a syncing account with NetNewsWire, you may want to hide or disable the On My Mac account.
### Hide the On My Mac account
To hide the On My Mac account, place the mouse over the **On My Mac** listing in the sidebar and click on **Hide**.
### Disable the On My Mac account
Disabling the On My Mac account can be done in NetNewsWires preferences.
1. Open the NetNewsWire preferences from **NetNewsWire → Preferences…** (⌘-,)
2. Click on **Accounts** and select *On My Mac*
3. Deselect the *Active* tick box
Youll no longer see the On My Mac account in the NetNewsWire sidebar.

View File

@@ -0,0 +1,16 @@
@title NetNewsWire and your privacy
# NetNewsWire and your privacy
[The NetNewsWire Privacy Policy](https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/privacypolicy.markdown)
NetNewsWire has a very strict privacy policy we dont want any private information about you.
The only data we collect about how you use NetNewsWire, *only if you agree and opt in*, is crash logs.
Changes to the Privacy Policy
-----------------------------
The [full privacy policy](https://github.com/brentsimmons/NetNewsWire/blob/master/Technotes/privacypolicy.markdown) is stored in the NetNewsWire GitHub repository, where you can track any changes we might make.

View File

@@ -9,21 +9,27 @@ One-handed reading is accomplished with keyboard shortcuts. NetNewsWires shor
(For a full list of keyboard shortcuts, including Command (⌘) key alternatives, see the [Keyboard Shortcuts](keyboard-shortcuts.html) help page, or use **Help → Keyboard Shortcuts** in NetNewsWire.)
## Use the Space bar
Use the Space bar
-----------------
The easiest way to read articles in NetNewsWire is using the **space bar**.
If youre reading an article that can be scrolled, pressing **space bar** will scroll it just like it would in Safari. If youre at the end of the article, **space bar** will take you to the next unread article.
## Go to Next Unread
Go to Next Unread
-----------------
Sometimes you may want to skip reading an article entirely thats totally fine! Rather than press space bar multiple times, you can jump directly to the next unread item by pressing ⌘-/ or just press **n** or **+**.
If you want to skip the article for now, but plan to come back to it later, the **m** key is for you. Pressing it will mark the current article as unread and then go to the *next unread* item.
## Moving around lists
Moving around lists
-------------------
You may not always want to read linearly imagine theres a new article on [Julia Evans](https://jvns.ca) site you want to read first. You still dont need to leave the keyboard.
@@ -32,7 +38,9 @@ Pressing the left and right arrow keys will move you between the three panes in
In the subscription and article lists, the up and down arrow keys will change the currently selected item.
## Marking as read or unread
Marking as read or unread
-------------------------
As you zip through the NetNewsWire interface, you may want to mark an article as read or unread. The easiest way to do this is use the **r** and **u** keys, respectively.

View File

@@ -2,10 +2,12 @@
# Installing and using the Safari Extension to add feeds
NetNewsWire provides a Safari Extension which adds a Subscribe to Feed button to your Safari toolbar. This allows you to quickly add a sites feed without entering an address manually into NetNewsWire.
NetNewsWire provides a Safari Extension which adds a *Subscribe to Feed* button to your Safari toolbar. This allows you to quickly add a sites feed without entering an address manually into NetNewsWire.
## Installing the NetNewsWire Safari Extension
Installing the NetNewsWire Safari Extension
-------------------------------------------
The Safari Extension is installed automatically with NetNewsWire. However, it must be *enabled* before you can use it.
@@ -17,16 +19,18 @@ You will enable the extension in Safari:
4. From the list, click the checkbox beside **Subscribe to Feed** to enable the extension
5. Close the Preferences window
Once this is done, the Subscribe to Feed button will be added to your Safari toolbar.
Once this is done, the *Subscribe to Feed* button will be added to your Safari toolbar.
## Adding a feed using the Safari Extension
Adding a feed using the Safari Extension
----------------------------------------
For any site that advertises its feeds, you can use the Subscribe to Feed button. Clicking it send the feeds address to NetNewsWire where you can set options like an alternative feed name, and the account and folder where it will be stored.
### What to do if the Subscribe to Feed button is greyed out and disabled
The Subscribe in NetNewsWire button will only be enabled for sites that advertise their feeds in their code. If the button is disabled, NetNewsWire wasnt able to find any feeds automatically.
The *Subscribe to Feed* button will only be enabled for sites that advertise their feeds in their code. If the button is disabled, NetNewsWire wasnt able to find any feeds automatically.
You may be able to find a feed manually by visiting the site. There, look for a link to an RSS, Atom or JSON feed. If one exists, you can [add this URL to NetNewsWire directly](adding-feeds.html). Right-click on the link and copy the URL to paste into NetNewsWire.

View File

@@ -0,0 +1,13 @@
@title Sorting the NetNewsWire timeline
# Sorting the NetNewsWire timeline
The NetNewsWire timeline is the list of articles shown in the centre pane.
« Screenshot »
By default, this list displays newer articles at the top.
You can change the sorting for the timeline in the View menu: **View → Sort by → Newest Article on Top** or **Oldest Article on Top**.
This setting applies to all feeds and can be changed anytime.

View File

@@ -0,0 +1,28 @@
@title How to Update NetNewsWire
# How to Update NetNewsWire
NetNewsWire can check for and install updated versions of itself. This can be done for you automatically in the background or you can check for updates manually.
Automatic checks for updates
----------------------------
When you first launch NetNewsWire youll be asked whether it should periodically check for updates. You will be notified of any new updates and can choose to update when youre ready.
Manually checking for updates
-----------------------------
You may have declined automatic checks for updates or just [read about a great new update](netnewswire-news.html) and want to download it now. You can always check for updates manually by selecting **NetNewsWire → Check for Updates…** from the menu bar.
If a new update is found, NetNewsWire can download and install it for you.
Privacy
-------
When checking for updates, whether automatically or manually, no personally-identifiable information is collected  not even an anonymous system profile. For more information about your privacy and NetNewsWire, please see the NetNewsWire Privacy Policy.

View File

@@ -0,0 +1,47 @@
@title Where NetNewsWire data is stored
# Where NetNewsWire data is stored
NetNewsWire remembers your feeds and article read status, as well as your account information and preferences. This information is stored in two different locations on your Mac, but separately from the NetNewsWire app.
Preferences file
----------------
Your NetNewsWire preferences file is called `com.ranchero.NetNewsWire-Evergreen.plist`. Its stored in your accounts Preferences folder, which can be found in the Finder by navigating to:
*Your home folder* Library Preferences
### Preferences caching
The contents in the preferences `plist` file are cached by macOS. If you make any changes or delete the file, log out and log back in before relaunching NetNewsWire to make sure the old settings are fully cleared. (Alternatively, if you prefer, you can also run `killall cfprefsd` from Terminal to reload the preferences cache.)
Feeds and accounts data
-----------------------
Information about your subscribed feeds, article read status and accounts (On my Mac and Feedbin) are stored in your accounts Application Support folder. This can be found in the Finder by navigating to:
*Your home folder* Library Application Support NetNewsWire
### Exporting your feeds
If you just want to export your list of subscribed feeds, NetNewsWire [can export an OPML for you](export-opml.html).
Resetting or removing NetNewsWire
---------------------------------
If you are running into problems while using NetNewsWire, you may want to reset your settings. By removing the files and folders above you can start over again from scratch.
You may also want to delete these files if you want to completely remove NetNewsWire from your Mac.
### Cant find the Library folder?
If you cant see the Library folder in the Finder, you can open it by holding down the Option (⌥) key and clicking **Go → Library**. For more information, see the [Library folder](https://support.apple.com/en-gb/guide/mac-help/aside/mh35934/10.14/mac/10.14) note in the macOS Documentation.

View File

@@ -12,7 +12,7 @@ Like this imaginary squished-down web page:
The good part is the part in the middle — thats the part with the news. Thats the part that you read. Thats the part youre interested in.
<p>And thats what RSS is — its just that part, minus the rest of the stuff.
And thats what RSS is — its just that part, minus the rest of the stuff.
#### Details

View File

@@ -56,13 +56,13 @@ class ScriptingTests: AppleScriptXCTestCase {
_ = doIndividualScript(filename: "testFeedOPML")
}
func testTitleOfArticlesWhoseScript() {
_ = doIndividualScript(filename: "testTitleOfArticlesWhose")
}
func testIterativeCreateAndDeleteScript() {
_ = doIndividualScriptWithExpectation(filename: "testIterativeCreateAndDeleteFeed")
}
// func testTitleOfArticlesWhoseScript() {
// _ = doIndividualScript(filename: "testTitleOfArticlesWhose")
// }
//
// func testIterativeCreateAndDeleteScript() {
// _ = doIndividualScriptWithExpectation(filename: "testIterativeCreateAndDeleteFeed")
// }
func doIndividualScriptWithExpectation(filename:String) {
let queue = DispatchQueue(label:"testQueue")

View File

@@ -1,11 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bqE-WD-JXz">
<device id="retina5_9" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14854.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="bqE-WD-JXz">
<device id="retina5_9" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14806.4"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -22,14 +19,14 @@
<tableViewSection id="3tl-Mb-Eno">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="lyJ-rf-8GA">
<rect key="frame" x="0.0" y="35" width="375" height="44"/>
<rect key="frame" x="0.0" y="1" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="lyJ-rf-8GA" id="eNS-Rp-w0A">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="URL" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="eRp-AP-WFq">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<rect key="frame" x="20" y="4" width="335" height="36"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" keyboardType="URL"/>
@@ -44,14 +41,14 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="Pxz-fv-QhQ">
<rect key="frame" x="0.0" y="79" width="375" height="44"/>
<rect key="frame" x="0.0" y="45" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Pxz-fv-QhQ" id="8aP-2A-8jc">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Title (Optional)" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="u7n-VL-Ho9">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<rect key="frame" x="20" y="4" width="335" height="36"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits"/>
@@ -70,20 +67,20 @@
<tableViewSection id="qn9-7O-LoA">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="MGg-y2-M2D">
<rect key="frame" x="0.0" y="129" width="375" height="44"/>
<rect key="frame" x="0.0" y="95" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="MGg-y2-M2D" id="sZh-wI-IW4">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Folder" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="grZ-g6-qfm">
<rect key="frame" x="19.999999999999996" y="4" width="48.666666666666657" height="35.666666666666664"/>
<rect key="frame" x="19.999999999999996" y="4" width="48.666666666666657" height="36"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vaV-kY-CaE">
<rect key="frame" x="313" y="11.666666666666666" width="42" height="20.333333333333336"/>
<rect key="frame" x="313" y="11.999999999999998" width="42" height="20.333333333333329"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -100,14 +97,14 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="140" id="PiN-2i-6Dj">
<rect key="frame" x="0.0" y="173" width="375" height="140"/>
<rect key="frame" x="0.0" y="139" width="375" height="140"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="PiN-2i-6Dj" id="sZ4-hj-gua">
<rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="140"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="v2n-nX-8jq">
<rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="140"/>
</pickerView>
</subviews>
<constraints>
@@ -171,7 +168,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="1Ce-E7-rG2">
<rect key="frame" x="112.66666666666669" y="108" width="150" height="29"/>
<rect key="frame" x="112.66666666666669" y="108" width="150" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="150" id="IEO-pf-4PB"/>
</constraints>
@@ -184,10 +181,10 @@
</connections>
</segmentedControl>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TUN-mZ-uLE">
<rect key="frame" x="0.0" y="156" width="375" height="656"/>
<rect key="frame" x="0.0" y="159" width="375" height="653"/>
</containerView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="TUN-mZ-uLE" secondAttribute="bottom" id="GrY-qn-cjm"/>
<constraint firstItem="TUN-mZ-uLE" firstAttribute="trailing" secondItem="pP4-52-FAl" secondAttribute="trailing" id="SJI-cb-6hr"/>
@@ -249,14 +246,14 @@
<tableViewSection id="12M-tp-EeV">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="XJI-0M-cAh">
<rect key="frame" x="0.0" y="35" width="375" height="44"/>
<rect key="frame" x="0.0" y="1" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XJI-0M-cAh" id="tIS-Tx-i17">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Name" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="lZK-wx-jbo">
<rect key="frame" x="20" y="4" width="335" height="35.666666666666664"/>
<rect key="frame" x="20" y="4" width="335" height="36"/>
<nil key="textColor"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits"/>
@@ -275,20 +272,20 @@
<tableViewSection id="bX9-Y2-S2D">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="44" id="uU0-Jh-goT">
<rect key="frame" x="0.0" y="85" width="375" height="44"/>
<rect key="frame" x="0.0" y="51" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="uU0-Jh-goT" id="y2g-dW-fPZ">
<rect key="frame" x="0.0" y="0.0" width="375" height="43.666666666666664"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YRf-I7-nkL">
<rect key="frame" x="20" y="4" width="64" height="35.666666666666664"/>
<rect key="frame" x="20" y="4" width="64" height="36"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mxj-Bw-Jfx">
<rect key="frame" x="313" y="11.666666666666666" width="42" height="20.333333333333336"/>
<rect key="frame" x="313" y="11.999999999999998" width="42" height="20.333333333333329"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@@ -305,14 +302,14 @@
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="140" id="zRi-p6-4KU">
<rect key="frame" x="0.0" y="129" width="375" height="140"/>
<rect key="frame" x="0.0" y="95" width="375" height="140"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zRi-p6-4KU" id="wek-Qh-OXr">
<rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="140"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<pickerView contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="eGY-V8-gzJ">
<rect key="frame" x="0.0" y="0.0" width="375" height="139.66666666666666"/>
<rect key="frame" x="0.0" y="0.0" width="375" height="140"/>
</pickerView>
</subviews>
<constraints>

View File

@@ -91,9 +91,12 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
}
delegate?.processingDidBegin()
let feedName = (nameTextField.text?.isEmpty ?? true) ? nil : nameTextField.text
BatchUpdate.shared.start()
account!.createFeed(url: url.absoluteString, name: nameTextField.text, container: container) { result in
account!.createFeed(url: url.absoluteString, name: feedName, container: container) { result in
BatchUpdate.shared.end()

View File

@@ -28,14 +28,12 @@ struct AppAssets {
static var chevronDownImage: UIImage = {
let image = UIImage(systemName: "chevron.down")!
return image
// return image.withTintColor(AppAssets.chevronDisclosureColor, renderingMode: .alwaysOriginal)
return image.withTintColor(AppAssets.chevronDisclosureColor, renderingMode: .alwaysOriginal)
}()
static var chevronRightImage: UIImage = {
let image = UIImage(systemName: "chevron.right")!
return image
// return image.withTintColor(AppAssets.chevronDisclosureColor, renderingMode: .alwaysOriginal)
return image.withTintColor(AppAssets.chevronDisclosureColor, renderingMode: .alwaysOriginal)
}()
static var faviconTemplateImage: RSImage = {
@@ -46,20 +44,18 @@ struct AppAssets {
return RSImage(named: "feedImage")!
}()
static var masterFolderColor: UIColor = {
return UIColor(named: "masterFolderColor")!
}()
static var masterFolderImage: UIImage = {
let image = UIImage(systemName: "folder.fill")!
return image
// return image.withTintColor(AppAssets.masterFolderColor, renderingMode: .alwaysOriginal)
return UIImage(systemName: "folder.fill")!
}()
static var netNewsWireBlueColor: UIColor = {
return UIColor(named: "netNewsWireBlueColor")!
}()
static var selectedTextColor: UIColor = {
return UIColor(named: "selectedTextColor")!
}()
static var settingsImage: UIImage = {
return UIImage(named: "settingsImage")!
}()
@@ -69,9 +65,7 @@ struct AppAssets {
}()
static var smartFeedImage: UIImage = {
let image = UIImage(systemName: "gear")!
return image
// return image.withTintColor(AppAssets.smartFeedColor, renderingMode: .alwaysOriginal)
return UIImage(systemName: "gear")!
}()
static var starColor: UIColor = {
@@ -88,8 +82,7 @@ struct AppAssets {
static var timelineStarImage: UIImage = {
let image = UIImage(systemName: "star.fill")!
return image
// return image.withTintColor(AppAssets.starColor, renderingMode: .alwaysOriginal)
return image.withTintColor(AppAssets.starColor, renderingMode: .alwaysOriginal)
}()
static var timelineUnreadCircleColor: UIColor = {

View File

@@ -34,8 +34,17 @@ class AppCoordinator: NSObject, UndoableCommandRunner {
private var masterTimelineViewController: MasterTimelineViewController?
private var detailViewController: DetailViewController? {
if let detailNavController = targetSplitForDetail().viewControllers.last as? UINavigationController {
return detailNavController.topViewController as? DetailViewController
if let detail = masterNavigationController.viewControllers.last as? DetailViewController {
return detail
}
if let subSplit = rootSplitViewController.viewControllers.last?.children.first as? UISplitViewController {
if let navController = subSplit.viewControllers.last as? UINavigationController {
return navController.topViewController as? DetailViewController
}
} else {
if let navController = rootSplitViewController.viewControllers.last?.children.first as? UINavigationController {
return navController.topViewController as? DetailViewController
}
}
return nil
}
@@ -61,8 +70,14 @@ class AppCoordinator: NSObject, UndoableCommandRunner {
return TreeController(delegate: treeControllerDelegate)
}()
var isRootSplitCollapsed: Bool {
return rootSplitViewController.isCollapsed
}
var isThreePanelMode: Bool {
return !rootSplitViewController.isCollapsed && rootSplitViewController.displayMode == .allVisible
return rootSplitViewController.traitCollection.userInterfaceIdiom == .pad &&
!rootSplitViewController.isCollapsed &&
rootSplitViewController.displayMode == .allVisible
}
var rootNode: Node {
@@ -210,14 +225,15 @@ class AppCoordinator: NSObject, UndoableCommandRunner {
rootSplitViewController.delegate = self
masterNavigationController = (rootSplitViewController.viewControllers.first as! UINavigationController)
masterNavigationController.delegate = self
masterFeedViewController = UIStoryboard.main.instantiateController(ofType: MasterFeedViewController.self)
masterFeedViewController.coordinator = self
masterNavigationController.pushViewController(masterFeedViewController, animated: false)
let systemMessageViewController = UIStoryboard.main.instantiateController(ofType: SystemMessageViewController.self)
let controller = addNavControllerIfNecessary(systemMessageViewController, split: rootSplitViewController, showBackButton: true)
rootSplitViewController.showDetailViewController(controller, sender: self)
let detailNavController = addNavControllerIfNecessary(systemMessageViewController, showButton: true)
let shimController = UIViewController()
shimController.replaceChildAndPinView(detailNavController)
rootSplitViewController.showDetailViewController(shimController, sender: self)
return rootSplitViewController
}
@@ -453,28 +469,24 @@ class AppCoordinator: NSObject, UndoableCommandRunner {
navControllerForTimeline().pushViewController(masterTimelineViewController!, animated: true)
}
if isThreePanelMode {
let systemMessageViewController = UIStoryboard.main.instantiateController(ofType: SystemMessageViewController.self)
let targetSplitController = targetSplitForDetail()
let controller = addNavControllerIfNecessary(systemMessageViewController, split: targetSplitController, showBackButton: false)
targetSplitController.showDetailViewController(controller, sender: self)
}
selectArticle(nil)
}
func selectArticle(_ indexPath: IndexPath) {
if detailViewController != nil {
currentArticleIndexPath = indexPath
} else {
let targetSplit = targetSplitForDetail()
func selectArticle(_ indexPath: IndexPath?) {
currentArticleIndexPath = indexPath
if indexPath == nil {
if !rootSplitViewController.isCollapsed {
let systemMessageViewController = UIStoryboard.main.instantiateController(ofType: SystemMessageViewController.self)
installDetailController(systemMessageViewController)
}
return
}
if detailViewController == nil {
let detailViewController = UIStoryboard.main.instantiateController(ofType: DetailViewController.self)
detailViewController.coordinator = self
let showBackButton = rootSplitViewController.displayMode != .allVisible
let controller = addNavControllerIfNecessary(detailViewController, split: targetSplit, showBackButton: showBackButton)
currentArticleIndexPath = indexPath
targetSplit.showDetailViewController(controller, sender: self)
installDetailController(detailViewController)
}
// Automatically hide the overlay
@@ -498,6 +510,10 @@ class AppCoordinator: NSObject, UndoableCommandRunner {
}
}
func selectFirstUnread() {
selectFirstUnreadArticleInTimeline()
}
func selectNextUnread() {
// This should never happen, but I don't want to risk throwing us
@@ -555,8 +571,8 @@ class AppCoordinator: NSObject, UndoableCommandRunner {
let settingsNavViewController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
settingsNavViewController.modalPresentationStyle = .formSheet
let settingsViewController = settingsNavViewController.topViewController as! SettingsViewController
settingsViewController.presentingParentController = masterFeedViewController
masterFeedViewController.present(settingsNavViewController, animated: true)
settingsViewController.presentingParentController = rootSplitViewController
rootSplitViewController.present(settingsNavViewController, animated: true)
// let settings = UIHostingController(rootView: SettingsView(viewModel: SettingsView.ViewModel()))
// self.present(settings, animated: true)
@@ -593,33 +609,82 @@ class AppCoordinator: NSObject, UndoableCommandRunner {
}
// MARK: UINavigationControllerDelegate
extension AppCoordinator: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if rootSplitViewController.isCollapsed != true && navigationController.viewControllers.count == 1 {
let systemMessageViewController = UIStoryboard.main.instantiateController(ofType: SystemMessageViewController.self)
let showBackButton = rootSplitViewController.displayMode != .allVisible
let controller = addNavControllerIfNecessary(systemMessageViewController, split: rootSplitViewController, showBackButton: showBackButton)
rootSplitViewController.showDetailViewController(controller, sender: self)
}
}
}
// MARK: UISplitViewControllerDelegate
extension AppCoordinator: UISplitViewControllerDelegate {
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
if currentArticle == nil {
// Return true to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return true
func splitViewController(_ splitViewController: UISplitViewController, willChangeTo displayMode: UISplitViewController.DisplayMode) {
guard splitViewController.traitCollection.userInterfaceIdiom == .pad && !splitViewController.isCollapsed else {
return
}
if splitViewController.displayMode != .allVisible && displayMode == .allVisible {
transitionToThreePanelMode()
}
if splitViewController.displayMode == .allVisible && displayMode != .allVisible {
transitionFromThreePanelMode()
}
return false
}
func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
// Check to see if the system is currently configured for three panel mode
if let subSplit = secondaryViewController.children.first as? UISplitViewController {
// Take the timeline controller out of the subsplit and throw it on the master navigation stack
if let masterTimelineNav = subSplit.viewControllers.first as? UINavigationController, let masterTimeline = masterTimelineNav.topViewController {
masterNavigationController.pushViewController(masterTimeline, animated: false)
}
// Take the detail view (ignoring system message controllers) and put it on the master navigation stack
if let detailNav = subSplit.viewControllers.last as? UINavigationController, let detail = detailNav.topViewController as? DetailViewController {
masterNavigationController.pushViewController(detail, animated: false)
}
} else {
// If the timeline controller has been initialized and only the feeds controller is on the stack, we add the timeline controller
if let timeline = masterTimelineViewController, masterNavigationController.viewControllers.count == 1 {
masterNavigationController.pushViewController(timeline, animated: false)
}
// Take the detail view (ignoring system message controllers) and put it on the master navigation stack
if let detailNav = secondaryViewController.children.first as? UINavigationController, let detail = detailNav.topViewController as? DetailViewController {
// I have no idea why, I have to wire up the left bar button item for this, but not when I am transitioning from three panel mode
detail.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem
detail.navigationItem.leftItemsSupplementBackButton = true
masterNavigationController.pushViewController(detail, animated: false)
}
}
return true
}
func splitViewController(_ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController? {
// If we are in three panel mode, return back the new shim controller that contains a new sub split controller
if isThreePanelMode {
return transitionToThreePanelMode()
}
if let detail = masterNavigationController.viewControllers.last as? DetailViewController {
// If we have a detail controller on the stack, remove it, wrap it in a shim, and return it.
masterNavigationController.viewControllers.removeLast()
let detailNav = addNavControllerIfNecessary(detail, showButton: true)
let shimController = UIViewController()
shimController.addChildAndPinView(detailNav)
return shimController
} else {
// Display a no selection controller since we don't have any detail selected
return fullyWrappedSystemMesssageController(showButton: true)
}
}
}
// MARK: Private
@@ -657,9 +722,13 @@ private extension AppCoordinator {
// MARK: Select Next Unread
@discardableResult
func selectFirstUnreadArticleInTimeline() -> Bool {
return selectArticleInTimeline(startingRow: 0)
}
@discardableResult
func selectNextUnreadArticleInTimeline() -> Bool {
let startingRow: Int = {
if let indexPath = currentArticleIndexPath {
return indexPath.row
@@ -668,10 +737,15 @@ private extension AppCoordinator {
}
}()
return selectArticleInTimeline(startingRow: startingRow)
}
func selectArticleInTimeline(startingRow: Int) -> Bool {
for i in startingRow..<articles.count {
let article = articles[i]
if !article.status.read {
currentArticleIndexPath = IndexPath(row: i, section: 0)
selectArticle(IndexPath(row: i, section: 0))
return true
}
}
@@ -844,50 +918,135 @@ private extension AppCoordinator {
// MARK: Double Split
func addNavControllerIfNecessary(_ controller: UIViewController, split: UISplitViewController, showBackButton: Bool) -> UIViewController {
if split.isCollapsed {
// Note about the Shim Controller
// In the root split view controller's secondary (or detail) position we use a view controller that
// only acts as a shim (or wrapper) for the actaully desired contents of the second position. This
// is because we normally can't change the root split view controllers second position contents
// during the display mode change callback (in the split view controller delegate). To fool the
// system, we leave the same controller, the shim, in place and change its child controllers instead.
func installDetailController(_ detailController: UIViewController) {
let showButton = rootSplitViewController.displayMode != .allVisible
let controller = addNavControllerIfNecessary(detailController, showButton: showButton)
if isThreePanelMode {
let targetSplit = ensureDoubleSplit().children.first as! UISplitViewController
targetSplit.showDetailViewController(controller, sender: self)
} else if rootSplitViewController.isCollapsed {
rootSplitViewController.showDetailViewController(controller, sender: self)
} else {
if let shimController = rootSplitViewController.viewControllers.last {
shimController.replaceChildAndPinView(controller)
}
}
}
func addNavControllerIfNecessary(_ controller: UIViewController, showButton: Bool) -> UIViewController {
if rootSplitViewController.isCollapsed {
return controller
} else {
let navController = UINavigationController(rootViewController: controller)
navController.isToolbarHidden = false
if showBackButton {
controller.navigationItem.leftBarButtonItem = split.displayModeButtonItem
if showButton {
controller.navigationItem.leftBarButtonItem = rootSplitViewController.displayModeButtonItem
controller.navigationItem.leftItemsSupplementBackButton = true
}
return navController
}
}
func ensureDoubleSplit() -> UISplitViewController {
if let subSplit = rootSplitViewController.viewControllers.last as? UISplitViewController {
return subSplit
func ensureDoubleSplit() -> UIViewController {
if let shimController = rootSplitViewController.viewControllers.last, shimController.children.first is UISplitViewController {
return shimController
}
rootSplitViewController.preferredPrimaryColumnWidthFraction = 0.33
rootSplitViewController.preferredPrimaryColumnWidthFraction = 0.30
let subSplit = UISplitViewController.template()
subSplit.delegate = self
subSplit.preferredDisplayMode = .allVisible
subSplit.preferredPrimaryColumnWidthFraction = 0.5
rootSplitViewController.showDetailViewController(subSplit, sender: self)
return subSplit
subSplit.preferredPrimaryColumnWidthFraction = 0.4285
let shimController = UIViewController()
shimController.addChildAndPinView(subSplit)
rootSplitViewController.showDetailViewController(shimController, sender: self)
return shimController
}
func navControllerForTimeline() -> UINavigationController {
if isThreePanelMode {
let subSplit = ensureDoubleSplit()
let subSplit = ensureDoubleSplit().children.first as! UISplitViewController
return subSplit.viewControllers.first as! UINavigationController
} else {
return masterNavigationController
}
}
func targetSplitForDetail() -> UISplitViewController {
if isThreePanelMode {
return ensureDoubleSplit()
func fullyWrappedSystemMesssageController(showButton: Bool) -> UIViewController {
let systemMessageViewController = UIStoryboard.main.instantiateController(ofType: SystemMessageViewController.self)
let navController = addNavControllerIfNecessary(systemMessageViewController, showButton: showButton)
let shimController = UIViewController()
shimController.addChildAndPinView(navController)
return shimController
}
@discardableResult
func transitionToThreePanelMode() -> UIViewController {
defer {
masterNavigationController.viewControllers = [masterFeedViewController]
}
if currentMasterIndexPath == nil && currentArticleIndexPath == nil {
let wrappedSystemMessageController = fullyWrappedSystemMesssageController(showButton: false)
rootSplitViewController.showDetailViewController(wrappedSystemMessageController, sender: self)
return wrappedSystemMessageController
} else {
return rootSplitViewController
let controller: UIViewController = {
if let result = detailViewController {
return result
} else {
return UIStoryboard.main.instantiateController(ofType: SystemMessageViewController.self)
}
}()
// Create the new sub split controller (wrapped in the shim of course) and add the timeline in the primary position
let shimController = ensureDoubleSplit()
let subSplit = shimController.children.first as! UISplitViewController
let masterTimelineNavController = subSplit.viewControllers.first as! UINavigationController
masterTimelineNavController.viewControllers = [masterTimelineViewController!]
// Put the detail or no selection controller in the secondary (or detail) position of the sub split
let navController = addNavControllerIfNecessary(controller, showButton: false)
subSplit.showDetailViewController(navController, sender: self)
return shimController
}
}
func transitionFromThreePanelMode() {
rootSplitViewController.preferredPrimaryColumnWidthFraction = UISplitViewController.automaticDimension
if let shimController = rootSplitViewController.viewControllers.last, let subSplit = shimController.children.first as? UISplitViewController {
// Push the timeline on to the master navigation controller. This should always be true if we have installed
// the sub split controller because we only install the sub split controller if a timeline needs to be displayed.
if let masterTimelineNav = subSplit.viewControllers.first as? UINavigationController, let masterTimeline = masterTimelineNav.topViewController {
masterNavigationController.pushViewController(masterTimeline, animated: false)
}
// Pull the detail or no selection controller out of the sub split second position and move it to the root split controller
// secondary (detail) position, by replacing the contents of the shim controller in the second position.
if let detailNav = subSplit.viewControllers.last as? UINavigationController, let topController = detailNav.topViewController {
let newNav = addNavControllerIfNecessary(topController, showButton: true)
shimController.replaceChildAndPinView(newNav)
}
}
}
}

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14845" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14854.2" 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="14799.2"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14806.4"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -24,7 +24,7 @@
</wkWebViewConfiguration>
</wkWebView>
</subviews>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="t8d-md-Yhc" firstAttribute="top" secondItem="VUw-jc-0yf" secondAttribute="top" id="0aK-ew-1HG"/>
<constraint firstItem="VUw-jc-0yf" firstAttribute="trailing" secondItem="t8d-md-Yhc" secondAttribute="trailing" id="31v-r8-kzh"/>
@@ -122,7 +122,6 @@
<!--System Message-->
<scene sceneID="tbo-yR-QVH">
<objects>
<placeholder placeholderIdentifier="IBFirstResponder" id="rgH-br-nLG" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
<viewController storyboardIdentifier="SystemMessageViewController" title="System Message" id="Xld-e9-xoL" customClass="SystemMessageViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5hz-HK-J2Q">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
@@ -130,12 +129,12 @@
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="System Message" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6C9-Jb-ZZR">
<rect key="frame" x="141.5" y="437.5" width="131" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="6C9-Jb-ZZR" firstAttribute="centerX" secondItem="5hz-HK-J2Q" secondAttribute="centerX" id="9iO-I1-FtK"/>
<constraint firstItem="6C9-Jb-ZZR" firstAttribute="centerY" secondItem="5hz-HK-J2Q" secondAttribute="centerY" id="jXm-OI-Igs"/>
@@ -148,6 +147,7 @@
<outlet property="messageLabel" destination="6C9-Jb-ZZR" id="HeR-Qv-yfz"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="rgH-br-nLG" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="872" y="-87"/>
</scene>
@@ -158,7 +158,7 @@
<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"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" rowHeight="208" id="T5d-L4-OKG" customClass="MasterTimelineTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="28" width="414" height="208"/>
@@ -238,7 +238,14 @@
</connections>
</barButtonItem>
</toolbarItems>
<navigationItem key="navigationItem" title="Feeds" id="Zdf-7t-Un8"/>
<navigationItem key="navigationItem" title="Feeds" id="Zdf-7t-Un8">
<barButtonItem key="leftBarButtonItem" image="settingsImage" id="TlU-Pg-ATe">
<connections>
<action selector="settings:" destination="7bK-jq-Zjz" id="Y8a-lz-Im7"/>
</connections>
</barButtonItem>
</navigationItem>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
<simulatedToolbarMetrics key="simulatedBottomBarMetrics"/>
<connections>
<outlet property="addNewItemButton" destination="yo6-w4-SfI" id="NbL-RH-N4J"/>
@@ -256,6 +263,7 @@
<image name="arrow.up" catalog="system" width="58" height="64"/>
<image name="circle" catalog="system" width="64" height="62"/>
<image name="safari" catalog="system" width="64" height="62"/>
<image name="settingsImage" width="24" height="24"/>
<image name="square.and.arrow.up" catalog="system" width="56" height="64"/>
<image name="star" catalog="system" width="64" height="58"/>
</resources>

View File

@@ -80,13 +80,6 @@ class DetailViewController: UIViewController {
let starImage = article.status.starred ? AppAssets.starClosedImage : AppAssets.starOpenImage
starBarButtonItem.image = starImage
if let timelineName = coordinator.timelineName {
if navigationController?.navigationItem.backBarButtonItem?.title != timelineName {
let backItem = UIBarButtonItem(title: timelineName, style: .plain, target: nil, action: nil)
navigationController?.navigationItem.backBarButtonItem = backItem
}
}
}
func reloadHTML() {

View File

@@ -105,6 +105,12 @@ class MasterFeedTableViewCell : UITableViewCell {
commonInit()
}
override func setSelected(_ selected: Bool, animated: Bool) {
titleView.textColor = selected ? AppAssets.selectedTextColor : UIColor.label
faviconImageView.tintColor = selected ? AppAssets.selectedTextColor : tintColor
super.setSelected(selected, animated: animated)
}
override func willTransition(to state: UITableViewCell.StateMask) {
super.willTransition(to: state)
showingEditControl = state.contains(.showingEditControl)

View File

@@ -29,11 +29,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
super.viewDidLoad()
let settingsButton = UIBarButtonItem()
settingsButton.image = AppAssets.settingsImage
settingsButton.action = #selector(settings(_:))
navigationItem.leftBarButtonItem = settingsButton
navigationItem.rightBarButtonItem = editButtonItem
tableView.register(MasterFeedTableViewSectionHeader.self, forHeaderFooterViewReuseIdentifier: "SectionHeader")
@@ -58,8 +53,8 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = true
navigationController?.title = NSLocalizedString("Feeds", comment: "Feeds")
clearsSelectionOnViewWillAppear = coordinator.isRootSplitCollapsed
super.viewWillAppear(animated)
}
@@ -104,7 +99,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
}
performBlockAndRestoreSelection {
tableView.reloadRows(at: [indexPath], with: .automatic)
tableView.reloadRows(at: [indexPath], with: .none)
}
}

View File

@@ -44,6 +44,17 @@ class MasterTimelineTableViewCell: UITableViewCell {
}
}
override func setSelected(_ selected: Bool, animated: Bool) {
let selectedTextColor = selected ? AppAssets.selectedTextColor : UIColor.label
titleView.textColor = selectedTextColor
summaryView.textColor = selectedTextColor
dateView.textColor = selectedTextColor
feedNameView.textColor = selectedTextColor
unreadIndicatorView.isSelected = selected
super.setSelected(selected, animated: animated)
}
override func sizeThatFits(_ size: CGSize) -> CGSize {
let layout = updatedLayout(width: size.width)
return CGSize(width: size.width, height: layout.height)

View File

@@ -10,6 +10,12 @@ import UIKit
class MasterUnreadIndicatorView: UIView {
var isSelected = false {
didSet {
setNeedsDisplay()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
self.isOpaque = false
@@ -26,7 +32,11 @@ class MasterUnreadIndicatorView: UIView {
}()
override func draw(_ dirtyRect: CGRect) {
AppAssets.timelineUnreadCircleColor.setFill()
if isSelected {
AppAssets.selectedTextColor.setFill()
} else {
AppAssets.timelineUnreadCircleColor.setFill()
}
MasterUnreadIndicatorView.bezierPath.fill()
}

View File

@@ -54,6 +54,11 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = coordinator.isRootSplitCollapsed
super.viewWillAppear(animated)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
becomeFirstResponder()
@@ -102,9 +107,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
}
@IBAction func firstUnread(_ sender: Any) {
if let indexPath = coordinator.firstUnreadArticleIndexPath {
tableView.scrollToRow(at: indexPath, at: .middle, animated: true)
}
coordinator.selectNextUnread()
}
// MARK: - Table view

View File

@@ -9,10 +9,10 @@
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "111",
"red" : "1.000",
"alpha" : "1.000",
"blue" : "250",
"green" : "175"
"blue" : "1.000",
"green" : "1.000"
}
}
}

View File

@@ -51,6 +51,7 @@ private extension AboutViewController {
textView.attributedText = string
textView.adjustsFontForContentSizeCategory = true
textView.font = .preferredFont(forTextStyle: .body)
textView.textColor = UIColor.label
}
}

View File

@@ -39,7 +39,7 @@ FRAMEWORK_SEARCH_PATHS = $(inherited) $(PROJECT_DIR)/Frameworks/Vendor
GCC_C_LANGUAGE_STANDARD = gnu11;
IPHONEOS_DEPLOYMENT_TARGET = 13.0
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks
INFOPLIST_FILE = iOS/Resources/Info.plist
PRODUCT_BUNDLE_IDENTIFIER = com.ranchero.NetNewsWire-Evergreen.iOS
PRODUCT_NAME = NetNewsWire