mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
@@ -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?) {
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 don’t 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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 you’d 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. We’ll coordinate there.
|
||||
|
||||
|
||||
@@ -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 that’s 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 feed’s folder
|
||||
|
||||
Choosing a feed’s folder
|
||||
------------------------
|
||||
|
||||
Before you add a feed, you can choose the account and folder where it will be saved.
|
||||
|
||||
This option is especially important if you’re 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 you’re 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 can’t find a feed
|
||||
|
||||
Sometimes NetNewsWire won’t be able to find a feed for a site. Either the site doesn’t offer a feed, or the feed isn’t advertised in a way that NetNewsWire can find it.
|
||||
What to do when NetNewsWire can’t find a feed
|
||||
---------------------------------------------
|
||||
|
||||
Sometimes NetNewsWire won’t be able to find a feed for a site. Either the site doesn’t offer a feed, or the feed isn’t 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 isn’t 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.
|
||||
@@ -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 you’d 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 NetNewsWire’s code or documentation, please refer to our [contribution guidelines](https://github.com/brentsimmons/NetNewsWire/blob/master/CONTRIBUTING.md) page on GitHub.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
55
Technotes/HelpBook/5.0/en/keyboard-shortcuts.markdown
Normal file
55
Technotes/HelpBook/5.0/en/keyboard-shortcuts.markdown
Normal file
@@ -0,0 +1,55 @@
|
||||
@title Keyboard shortcuts
|
||||
|
||||
# Keyboard shortcuts
|
||||
|
||||
NetNewsWire’s 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. There’s 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 NetNewsWire’s 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. *You’ll be a NetNewsWire keyboard magician in no time.*
|
||||
|
||||
|
||||
### Do I need to use the Command key?
|
||||
|
||||
No, the Command (⌘) key isn’t required for these advanced keyboard shortcuts – just press the key by itself.
|
||||
|
||||
|
||||
|
||||
NetNewsWire’s keyboard shortcuts
|
||||
--------------------------------
|
||||
|
||||
Many of NetNewsWire’s 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 ⏎ 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 ⌥+←</td></tr>
|
||||
<tr><td>Expand</td> <td>. or ⌥+→</td></tr>
|
||||
<tr><td>Collapse All (except for group items)</td> <td>; or ⌥+⌘+←</td></tr>
|
||||
<tr><td>Expand All</td> <td>' or ⌥+⌘+→</td></tr>
|
||||
<tr><td>Move focus to headlines</td> <td>→</td></tr>
|
||||
<tr><th>Article list timeline (centre pane)</th></tr>
|
||||
<tr><td>Move focus to subscriptions</td> <td>←</td></tr>
|
||||
<tr><td>Move focus to detail</td> <td>→</td></tr>
|
||||
<tr><th>Article reader (right pane)</th></tr>
|
||||
<tr><td>Move focus to headlines</td> <td>←</td></tr>
|
||||
</table>
|
||||
@@ -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 it’s no longer in your list, it’s easy to add it back.
|
||||
|
||||
|
||||
@@ -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. It’s 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 don’t 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 NetNewsWire’s preferences.
|
||||
|
||||
1. Open the NetNewsWire preferences from **NetNewsWire → Preferences…** (⌘-,)
|
||||
2. Click on **Accounts** and select *On My Mac*
|
||||
3. Deselect the *Active* tick box
|
||||
|
||||
You’ll no longer see the On My Mac account in the NetNewsWire sidebar.
|
||||
16
Technotes/HelpBook/5.0/en/privacy.markdown
Normal file
16
Technotes/HelpBook/5.0/en/privacy.markdown
Normal 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 don’t 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.
|
||||
@@ -9,21 +9,27 @@ One-handed reading is accomplished with keyboard shortcuts. NetNewsWire’s 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 you’re reading an article that can be scrolled, pressing **space bar** will scroll it – just like it would in Safari. If you’re 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 – that’s 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 there’s a new article on [Julia Evans’](https://jvns.ca) site you want to read first. You still don’t 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.
|
||||
|
||||
|
||||
@@ -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 site’s 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 site’s 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 feed’s 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 wasn’t 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 wasn’t 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.
|
||||
13
Technotes/HelpBook/5.0/en/sorting-the-timeline.markdown
Normal file
13
Technotes/HelpBook/5.0/en/sorting-the-timeline.markdown
Normal 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.
|
||||
28
Technotes/HelpBook/5.0/en/updating.markdown
Normal file
28
Technotes/HelpBook/5.0/en/updating.markdown
Normal 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 you’ll be asked whether it should periodically check for updates. You will be notified of any new updates and can choose to update when you’re 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.
|
||||
47
Technotes/HelpBook/5.0/en/userdata-location.markdown
Normal file
47
Technotes/HelpBook/5.0/en/userdata-location.markdown
Normal 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`. It’s stored in your account’s 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 account’s 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.
|
||||
|
||||
|
||||
### Can’t find the Library folder?
|
||||
|
||||
If you can’t 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.
|
||||
@@ -12,7 +12,7 @@ Like this imaginary squished-down web page:
|
||||
|
||||
The good part is the part in the middle — that’s the part with the news. That’s the part that you read. That’s the part you’re interested in.
|
||||
|
||||
<p>And that’s what RSS is — it’s just that part, minus the rest of the stuff.
|
||||
And that’s what RSS is — it’s just that part, minus the rest of the stuff.
|
||||
|
||||
#### Details
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,7 @@ private extension AboutViewController {
|
||||
textView.attributedText = string
|
||||
textView.adjustsFontForContentSizeCategory = true
|
||||
textView.font = .preferredFont(forTextStyle: .body)
|
||||
textView.textColor = UIColor.label
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Submodule submodules/RSCore updated: 277c64868a...b8656655f6
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user