Merge branch 'mac-release' into main

This commit is contained in:
Maurice Parker 2020-10-18 16:37:00 -05:00
commit 4284ea26c8
75 changed files with 654 additions and 267 deletions

View File

@ -250,7 +250,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return delegate.refreshProgress
}
init?(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) {
init(dataFolder: String, type: AccountType, accountID: String, transport: Transport? = nil) {
switch type {
case .onMyMac:
self.delegate = LocalAccountDelegate()

View File

@ -104,7 +104,7 @@ public final class AccountManager: UnreadCountProvider {
abort()
}
defaultAccount = Account(dataFolder: localAccountFolder, type: .onMyMac, accountID: defaultAccountIdentifier)!
defaultAccount = Account(dataFolder: localAccountFolder, type: .onMyMac, accountID: defaultAccountIdentifier)
accountsDictionary[defaultAccount.accountID] = defaultAccount
readAccountsFromDisk()
@ -131,7 +131,7 @@ public final class AccountManager: UnreadCountProvider {
abort()
}
let account = Account(dataFolder: accountFolder, type: type, accountID: accountID)!
let account = Account(dataFolder: accountFolder, type: type, accountID: accountID)
accountsDictionary[accountID] = account
var userInfo = [String: Any]()
@ -166,6 +166,18 @@ public final class AccountManager: UnreadCountProvider {
NotificationCenter.default.post(name: .UserDidDeleteAccount, object: self, userInfo: userInfo)
}
public func duplicateServiceAccount(type: AccountType, username: String?) -> Bool {
guard type != .onMyMac else {
return false
}
for account in accounts {
if account.type == type && username == account.username {
return true
}
}
return false
}
public func existingAccount(with accountID: String) -> Account? {
return accountsDictionary[accountID]
}
@ -423,16 +435,24 @@ private extension AccountManager {
print("Error reading Accounts folder: \(error)")
return
}
filenames = filenames?.sorted()
filenames?.forEach { (oneFilename) in
guard oneFilename != defaultAccountFolderName else {
return
}
if let oneAccount = loadAccount(oneFilename) {
accountsDictionary[oneAccount.accountID] = oneAccount
if !duplicateServiceAccount(oneAccount) {
accountsDictionary[oneAccount.accountID] = oneAccount
}
}
}
}
func duplicateServiceAccount(_ account: Account) -> Bool {
return duplicateServiceAccount(type: account.type, username: account.username)
}
func sortByName(_ accounts: [Account]) -> [Account] {
// LocalAccount is first.

View File

@ -20,3 +20,19 @@ public protocol Feed: FeedIdentifiable, ArticleFetcher, DisplayNameProvider, Unr
var defaultReadFilterType: ReadFilterType { get }
}
public extension Feed {
func readFiltered(readFilterEnabledTable: [FeedIdentifier: Bool]) -> Bool {
guard defaultReadFilterType != .alwaysRead else {
return true
}
if let feedID = feedID, let readFilterEnabled = readFilterEnabledTable[feedID] {
return readFilterEnabled
} else {
return defaultReadFilterType == .read
}
}
}

View File

@ -48,10 +48,15 @@ final class FeedlyAPICaller {
private let transport: Transport
private let baseUrlComponents: URLComponents
private let uriComponentAllowed: CharacterSet
init(transport: Transport, api: API) {
self.transport = transport
self.baseUrlComponents = api.baseUrlComponents
var urlHostAllowed = CharacterSet.urlHostAllowed
urlHostAllowed.remove("+")
uriComponentAllowed = urlHostAllowed
}
weak var delegate: FeedlyAPICallerDelegate?
@ -272,7 +277,7 @@ final class FeedlyAPICaller {
}
private func encodeForURLPath(_ pathComponent: String) -> String? {
return pathComponent.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
return pathComponent.addingPercentEncoding(withAllowedCharacters: uriComponentAllowed)
}
func deleteCollection(with id: String, completion: @escaping (Result<Void, Error>) -> ()) {

View File

@ -15,6 +15,13 @@ public protocol OAuthAccountAuthorizationOperationDelegate: class {
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error)
}
public enum OAuthAccountAuthorizationOperationError: LocalizedError {
case duplicateAccount
public var errorDescription: String? {
return NSLocalizedString("There is already a Feedly account with that username created.", comment: "Duplicate Error")
}
}
@objc public final class OAuthAccountAuthorizationOperation: NSObject, MainThreadOperation, ASWebAuthenticationPresentationContextProviding {
public var isCanceled: Bool = false {
@ -64,10 +71,26 @@ public protocol OAuthAccountAuthorizationOperationDelegate: class {
self?.didEndAuthentication(url: url, error: error)
}
}
self.session = session
session.presentationContextProvider = self
session.start()
guard session.start() else {
/// Documentation does not say on why `ASWebAuthenticationSession.start` or `canStart` might return false.
/// Perhaps it has something to do with an inter-process communication failure? No browsers installed? No browsers that support web authentication?
struct UnableToStartASWebAuthenticationSessionError: LocalizedError {
let errorDescription: String? = NSLocalizedString("Unable to start a web authentication session with the default web browser.",
comment: "OAuth - error description - unable to authorize because ASWebAuthenticationSession did not start.")
let recoverySuggestion: String? = NSLocalizedString("Check your default web browser in System Preferences or change it to Safari and try again.",
comment: "OAuth - recovery suggestion - ensure browser selected supports web authentication.")
}
didFinish(UnableToStartASWebAuthenticationSessionError())
return
}
self.session = session
}
public func cancel() {
@ -122,7 +145,11 @@ public protocol OAuthAccountAuthorizationOperationDelegate: class {
}
private func saveAccount(for grant: OAuthAuthorizationGrant) {
// TODO: Find an already existing account for this username?
guard !AccountManager.shared.duplicateServiceAccount(type: .feedly, username: grant.accessToken.username) else {
didFinish(OAuthAccountAuthorizationOperationError.duplicateAccount)
return
}
let account = AccountManager.shared.createAccount(type: .feedly)
do {

View File

@ -67,6 +67,13 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
addCompletionHandler = nil
super.didCancel()
}
override func didFinish(with error: Error) {
assert(Thread.isMainThread)
addCompletionHandler?(.failure(error))
addCompletionHandler = nil
super.didFinish(with: error)
}
func feedlySearchOperation(_ operation: FeedlySearchOperation, didGet response: FeedlyFeedsSearchResponse) {
guard !isCanceled else {

View File

@ -32,7 +32,7 @@ final class FeedlyGetCollectionsOperation: FeedlyOperation, FeedlyCollectionProv
service.getCollections { result in
switch result {
case .success(let collections):
os_log(.debug, log: self.log, "Received collections: %@.", collections.map { $0.id })
os_log(.debug, log: self.log, "Received collections: %{public}@", collections.map { $0.id })
self.collections = collections
self.didFinish()

View File

@ -51,13 +51,13 @@ final class FeedlySyncStreamContentsOperation: FeedlyOperation, FeedlyOperationD
}
override func didCancel() {
os_log(.debug, log: log, "Canceling sync stream contents")
os_log(.debug, log: log, "Canceling sync stream contents for %{public}@", resource.id)
operationQueue.cancelAllOperations()
super.didCancel()
}
func enqueueOperations(for continuation: String?) {
os_log(.debug, log: log, "Requesting page for %@", resource.id)
os_log(.debug, log: log, "Requesting page for %{public}@", resource.id)
let operations = pageOperations(for: continuation)
operationQueue.addOperations(operations)
}
@ -91,14 +91,14 @@ final class FeedlySyncStreamContentsOperation: FeedlyOperation, FeedlyOperationD
func feedlyGetStreamContentsOperation(_ operation: FeedlyGetStreamContentsOperation, didGetContentsOf stream: FeedlyStream) {
guard !isCanceled else {
os_log(.debug, log: log, "Cancelled requesting page for %@", resource.id)
os_log(.debug, log: log, "Cancelled requesting page for %{public}@", resource.id)
return
}
os_log(.debug, log: log, "Ingesting %i items from %@", stream.items.count, stream.id)
os_log(.debug, log: log, "Ingesting %i items from %{public}@", stream.items.count, stream.id)
guard isPagingEnabled, let continuation = stream.continuation else {
os_log(.debug, log: log, "Reached end of stream for %@", stream.id)
os_log(.debug, log: log, "Reached end of stream for %{public}@", stream.id)
return
}
@ -106,7 +106,7 @@ final class FeedlySyncStreamContentsOperation: FeedlyOperation, FeedlyOperationD
}
func feedlyCheckpointOperationDidReachCheckpoint(_ operation: FeedlyCheckpointOperation) {
os_log(.debug, log: log, "Completed ingesting items from %@", resource.id)
os_log(.debug, log: log, "Completed ingesting items from %{public}@", resource.id)
didFinish()
}

View File

@ -135,16 +135,6 @@ extension LocalAccountRefresher: DownloadSessionDelegate {
return false
}
if data.count > 4096 {
let parserData = ParserData(url: feed.url, data: data)
if FeedParser.mightBeAbleToParseBasedOnPartialData(parserData) {
return true
} else {
delegate?.localAccountRefresher(self, requestCompletedFor: feed)
return false
}
}
return true
}

View File

@ -19,6 +19,6 @@
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Ranchero Software. All rights reserved.</string>
<string>Copyright © 2019-2020 Brent Simmons. All rights reserved.</string>
</dict>
</plist>

View File

@ -194,7 +194,12 @@ final class AppDefaults {
}
var hideDockUnreadCount: Bool {
return AppDefaults.bool(for: Key.hideDockUnreadCount)
get {
return AppDefaults.bool(for: Key.hideDockUnreadCount)
}
set {
AppDefaults.setBool(for: Key.hideDockUnreadCount, newValue)
}
}
#if !MAC_APP_STORE

View File

@ -240,6 +240,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
refreshTimer = AccountRefreshTimer()
syncTimer = ArticleStatusSyncTimer()
UNUserNotificationCenter.current().requestAuthorization(options:[.badge]) { (granted, error) in }
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
if settings.authorizationStatus == .authorized {
DispatchQueue.main.async {
@ -620,7 +622,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
@IBAction func showHelp(_ sender: Any?) {
Browser.open("https://ranchero.com/netnewswire/help/mac/5.0/en/", inBackground: false)
Browser.open("https://ranchero.com/netnewswire/help/mac/5.1/en/", inBackground: false)
}
@IBAction func donateToAppCampForGirls(_ sender: Any?) {

View File

@ -328,7 +328,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
<prototypeCellViews>
<tableCellView identifier="HeaderCell" id="qkt-WA-5tB">
<rect key="frame" x="11" y="0.0" width="217" height="17"/>
<rect key="frame" x="1" y="0.0" width="237" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fNJ-z1-0Up">
@ -346,7 +346,7 @@
</connections>
</tableCellView>
<tableCellView identifier="DataCell" id="HJn-Tm-YNO" customClass="SidebarCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="11" y="17" width="217" height="17"/>
<rect key="frame" x="1" y="17" width="237" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</tableCellView>
</prototypeCellViews>
@ -505,7 +505,7 @@
</popUpButtonCell>
</popUpButton>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="iA5-go-AO0">
<rect key="frame" x="350" y="127" width="13" height="13"/>
<rect key="frame" x="350" y="179" width="13" height="14"/>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" image="filterInactive" imagePosition="overlaps" alignment="center" imageScaling="proportionallyDown" inset="2" id="j7d-36-DO5">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17147" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="mPU-HG-I4u">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="mPU-HG-I4u">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17147"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17156"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -10,7 +10,7 @@
<scene sceneID="NTj-pB-UZ0">
<objects>
<windowController showSeguePresentationStyle="single" id="mPU-HG-I4u" customClass="PreferencesWindowController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" tabbingMode="disallowed" toolbarStyle="preference" id="2C0-LP-36T">
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" tabbingMode="disallowed" id="2C0-LP-36T">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" topStrut="YES"/>
<rect key="contentRect" x="128" y="512" width="480" height="273"/>
@ -32,14 +32,14 @@
<objects>
<viewController title="General" storyboardIdentifier="General" id="iuH-lz-18x" customClass="GeneralPreferencesViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="WnV-px-wCT">
<rect key="frame" x="0.0" y="0.0" width="501" height="217"/>
<rect key="frame" x="0.0" y="0.0" width="506" height="210"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Ut3-yd-q6G">
<rect key="frame" x="80" y="16" width="340" height="185"/>
<rect key="frame" x="36" y="16" width="433" height="178"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ucw-vG-yLt">
<rect key="frame" x="29" y="162" width="92" height="16"/>
<rect key="frame" x="53" y="157" width="92" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Refresh feeds:" id="F7c-lm-g97">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -47,7 +47,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SFF-mL-yc8">
<rect key="frame" x="125" y="157" width="218" height="25"/>
<rect key="frame" x="148" y="150" width="289" height="25"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="N1a-qV-4Os"/>
</constraints>
@ -81,32 +81,32 @@
</connections>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rbK-cS-VQl">
<rect key="frame" x="-2" y="131" width="123" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Default RSS reader:" id="bUb-r3-SmS">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Wsb-Lr-8Q7">
<rect key="frame" x="87" y="91" width="58" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Browser:" id="CgU-dE-Qtb">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="cSu-T2-Jby">
<rect key="frame" x="125" y="126" width="218" height="25"/>
<popUpButtonCell key="cell" type="push" title="NetNewsWire" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="bEy-Qx-Grl" id="Dyk-WN-XOo" userLabel="Popup">
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ci4-fW-KjU">
<rect key="frame" x="148" y="84" width="289" height="25"/>
<popUpButtonCell key="cell" type="push" title="Safari" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="ObP-qK-qDJ" id="hrm-aT-Rc2" userLabel="Popup">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="8PF-kj-u99">
<menu key="menu" id="j1i-Ev-7rI">
<items>
<menuItem title="NetNewsWire" state="on" id="bEy-Qx-Grl"/>
<menuItem title="Safari" state="on" id="ObP-qK-qDJ"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="rssReaderPopupDidChangeValue:" target="iuH-lz-18x" id="pyX-Bp-ws0"/>
<action selector="browserPopUpDidChangeValue:" target="iuH-lz-18x" id="vSr-98-LQL"/>
</connections>
</popUpButton>
<button verticalHuggingPriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Ubm-Pk-l7x">
<rect key="frame" x="125" y="67" width="215" height="18"/>
<buttonCell key="cell" type="check" title="Open in background in browser" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="t0a-LN-rCv">
<button horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Ubm-Pk-l7x">
<rect key="frame" x="149" y="57" width="284" height="18"/>
<buttonCell key="cell" type="check" title="Open web pages in background in browser" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="t0a-LN-rCv">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
@ -125,7 +125,7 @@
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jVd-Ie-CGX">
<rect key="frame" x="53" y="4" width="68" height="16"/>
<rect key="frame" x="77" y="4" width="68" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Dock icon:" id="vFc-Nz-RFp">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -133,91 +133,81 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mwT-nY-TrX">
<rect key="frame" x="125" y="3" width="143" height="18"/>
<rect key="frame" x="149" y="3" width="143" height="18"/>
<buttonCell key="cell" type="check" title="Show unread count" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="lh0-G6-9v4">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<binding destination="mAF-gO-1PI" name="value" keyPath="values.JustinMillerHideDockUnreadCount" id="J0i-eh-Rqq">
<dictionary key="options">
<bool key="NSAllowsEditingMultipleValuesSelection" value="NO"/>
<bool key="NSConditionallySetsEnabled" value="NO"/>
<integer key="NSMultipleValuesPlaceholder" value="1"/>
<integer key="NSNoSelectionPlaceholder" value="1"/>
<integer key="NSNotApplicablePlaceholder" value="1"/>
<integer key="NSNullPlaceholder" value="1"/>
<bool key="NSRaisesForNotApplicableKeys" value="NO"/>
<string key="NSValueTransformerName">NSNegateBoolean</string>
</dictionary>
</binding>
<action selector="toggleShowingUnreadCount:" target="iuH-lz-18x" id="CfQ-Pv-9d2"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="j0t-Wa-UTL">
<rect key="frame" x="145" y="32" width="197" height="28"/>
<textFieldCell key="cell" controlSize="small" title="Hold the Shift key to invert this preference." id="EMq-9M-zTJ">
<rect key="frame" x="168" y="34" width="235" height="16"/>
<textFieldCell key="cell" controlSize="small" title="Press the Shift key to do the opposite." id="EMq-9M-zTJ">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ci4-fW-KjU">
<rect key="frame" x="125" y="95" width="218" height="25"/>
<popUpButtonCell key="cell" type="push" title="Safari" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="ObP-qK-qDJ" id="hrm-aT-Rc2" userLabel="Popup">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ulQ-kB-eNO">
<rect key="frame" x="18" y="124" width="127" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Default RSS Reader:" id="Ls3-nS-E6w">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="48p-6Z-Yid">
<rect key="frame" x="148" y="118" width="289" height="25"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Fex-Ru-tjJ" id="Xz6-ZO-abi">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="j1i-Ev-7rI">
<menu key="menu" id="maY-sW-bmj">
<items>
<menuItem title="Safari" state="on" id="ObP-qK-qDJ"/>
<menuItem title="Item 1" state="on" id="Fex-Ru-tjJ"/>
<menuItem title="Item 2" id="IKn-RA-vwL"/>
<menuItem title="Item 3" id="n2E-vh-TSH"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="browserPopUpDidChangeValue:" target="iuH-lz-18x" id="vSr-98-LQL"/>
<action selector="rssReaderPopupDidChangeValue:" target="iuH-lz-18x" id="wH0-4N-Exe"/>
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Wsb-Lr-8Q7">
<rect key="frame" x="45" y="100" width="76" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Web pages:" id="CgU-dE-Qtb">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="Ubm-Pk-l7x" firstAttribute="width" secondItem="SFF-mL-yc8" secondAttribute="width" id="0dn-WI-rjt"/>
<constraint firstItem="Wsb-Lr-8Q7" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="17A-5m-ZG0"/>
<constraint firstAttribute="trailing" secondItem="Ubm-Pk-l7x" secondAttribute="trailing" id="3h4-m7-pMW"/>
<constraint firstAttribute="trailing" secondItem="cSu-T2-Jby" secondAttribute="trailing" id="4L1-zz-ztr"/>
<constraint firstItem="cSu-T2-Jby" firstAttribute="width" secondItem="SFF-mL-yc8" secondAttribute="width" id="4Zo-pu-jyl"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="Ubm-Pk-l7x" secondAttribute="trailing" id="3h4-m7-pMW"/>
<constraint firstItem="48p-6Z-Yid" firstAttribute="leading" secondItem="ulQ-kB-eNO" secondAttribute="trailing" constant="8" symbolic="YES" id="4hb-gZ-DtU"/>
<constraint firstItem="mwT-nY-TrX" firstAttribute="firstBaseline" secondItem="jVd-Ie-CGX" secondAttribute="firstBaseline" id="5nL-J8-5as"/>
<constraint firstAttribute="trailing" secondItem="j0t-Wa-UTL" secondAttribute="trailing" id="7Oh-pf-X12"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="j0t-Wa-UTL" secondAttribute="trailing" id="7Oh-pf-X12"/>
<constraint firstItem="Ubm-Pk-l7x" firstAttribute="leading" secondItem="SFF-mL-yc8" secondAttribute="leading" id="7cy-O4-Zz2"/>
<constraint firstItem="Ci4-fW-KjU" firstAttribute="width" secondItem="cSu-T2-Jby" secondAttribute="width" id="AiX-qu-kU5"/>
<constraint firstItem="Ci4-fW-KjU" firstAttribute="width" secondItem="SFF-mL-yc8" secondAttribute="width" id="AE4-am-IWK"/>
<constraint firstItem="Ci4-fW-KjU" firstAttribute="top" secondItem="48p-6Z-Yid" secondAttribute="bottom" constant="14" id="Ay8-21-Xwi"/>
<constraint firstItem="Ubm-Pk-l7x" firstAttribute="top" secondItem="Ci4-fW-KjU" secondAttribute="bottom" constant="14" id="GNx-7d-yAo"/>
<constraint firstItem="Wsb-Lr-8Q7" firstAttribute="trailing" secondItem="jVd-Ie-CGX" secondAttribute="trailing" id="ITg-ay-Y2x"/>
<constraint firstItem="cSu-T2-Jby" firstAttribute="leading" secondItem="SFF-mL-yc8" secondAttribute="leading" id="Mk0-6R-yC3"/>
<constraint firstAttribute="trailing" secondItem="SFF-mL-yc8" secondAttribute="trailing" id="N39-Q9-X5Q"/>
<constraint firstItem="j0t-Wa-UTL" firstAttribute="leading" secondItem="Ubm-Pk-l7x" secondAttribute="leading" constant="20" id="UKq-8p-lyR"/>
<constraint firstItem="j0t-Wa-UTL" firstAttribute="top" secondItem="Ubm-Pk-l7x" secondAttribute="bottom" constant="8" symbolic="YES" id="XTw-Ef-FD3"/>
<constraint firstItem="Ubm-Pk-l7x" firstAttribute="width" secondItem="SFF-mL-yc8" secondAttribute="width" id="TX4-iO-J5E"/>
<constraint firstItem="j0t-Wa-UTL" firstAttribute="leading" secondItem="Ubm-Pk-l7x" secondAttribute="leading" constant="19" id="UKq-8p-lyR"/>
<constraint firstItem="ulQ-kB-eNO" firstAttribute="leading" secondItem="Ut3-yd-q6G" secondAttribute="leading" constant="20" symbolic="YES" id="X9W-fv-Cho"/>
<constraint firstItem="j0t-Wa-UTL" firstAttribute="top" secondItem="Ubm-Pk-l7x" secondAttribute="bottom" constant="8" id="XTw-Ef-FD3"/>
<constraint firstItem="48p-6Z-Yid" firstAttribute="width" secondItem="SFF-mL-yc8" secondAttribute="width" id="Yin-Gz-EcF"/>
<constraint firstItem="SFF-mL-yc8" firstAttribute="firstBaseline" secondItem="ucw-vG-yLt" secondAttribute="firstBaseline" id="aqn-St-DJy"/>
<constraint firstItem="48p-6Z-Yid" firstAttribute="leading" secondItem="SFF-mL-yc8" secondAttribute="leading" id="cvo-WU-UfZ"/>
<constraint firstItem="jVd-Ie-CGX" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="dDb-jw-vca"/>
<constraint firstItem="Ci4-fW-KjU" firstAttribute="top" secondItem="cSu-T2-Jby" secondAttribute="bottom" constant="10" symbolic="YES" id="dVG-r1-K02"/>
<constraint firstItem="cSu-T2-Jby" firstAttribute="top" secondItem="SFF-mL-yc8" secondAttribute="bottom" constant="10" symbolic="YES" id="fI9-cF-7sm"/>
<constraint firstItem="48p-6Z-Yid" firstAttribute="top" secondItem="SFF-mL-yc8" secondAttribute="bottom" constant="12" id="dvh-Bx-K9f"/>
<constraint firstItem="SFF-mL-yc8" firstAttribute="top" secondItem="Ut3-yd-q6G" secondAttribute="top" constant="4" id="fXo-df-bJh"/>
<constraint firstItem="mwT-nY-TrX" firstAttribute="leading" secondItem="Ubm-Pk-l7x" secondAttribute="leading" id="fb7-Og-rSp"/>
<constraint firstItem="ucw-vG-yLt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="gJ9-Cu-GzZ"/>
<constraint firstItem="rbK-cS-VQl" firstAttribute="leading" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="jBC-cr-zIT"/>
<constraint firstItem="ulQ-kB-eNO" firstAttribute="centerY" secondItem="48p-6Z-Yid" secondAttribute="centerY" id="gSz-1T-Zp7"/>
<constraint firstAttribute="bottom" secondItem="mwT-nY-TrX" secondAttribute="bottom" constant="4" id="jFE-ye-pSr"/>
<constraint firstItem="rbK-cS-VQl" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="k6n-MY-jmI"/>
<constraint firstItem="mwT-nY-TrX" firstAttribute="top" secondItem="j0t-Wa-UTL" secondAttribute="bottom" constant="12" id="nE9-nH-ueT"/>
<constraint firstItem="cSu-T2-Jby" firstAttribute="leading" secondItem="rbK-cS-VQl" secondAttribute="trailing" constant="8" symbolic="YES" id="o6s-o6-i3O"/>
<constraint firstItem="ucw-vG-yLt" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="lDL-JN-ANP"/>
<constraint firstItem="mwT-nY-TrX" firstAttribute="top" secondItem="j0t-Wa-UTL" secondAttribute="bottom" constant="14" id="nE9-nH-ueT"/>
<constraint firstItem="Wsb-Lr-8Q7" firstAttribute="firstBaseline" secondItem="Ci4-fW-KjU" secondAttribute="firstBaseline" id="rPX-je-OG5"/>
<constraint firstItem="Ci4-fW-KjU" firstAttribute="leading" secondItem="Wsb-Lr-8Q7" secondAttribute="trailing" constant="8" symbolic="YES" id="rcx-B6-zLP"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="mwT-nY-TrX" secondAttribute="trailing" id="skS-m8-bVR"/>
<constraint firstItem="cSu-T2-Jby" firstAttribute="firstBaseline" secondItem="rbK-cS-VQl" secondAttribute="firstBaseline" id="vdQ-R6-ZDZ"/>
<constraint firstItem="SFF-mL-yc8" firstAttribute="leading" secondItem="ucw-vG-yLt" secondAttribute="trailing" constant="8" symbolic="YES" id="yBm-Dc-lGA"/>
<constraint firstAttribute="trailing" secondItem="48p-6Z-Yid" secondAttribute="trailing" id="zQB-Eg-KRJ"/>
<constraint firstAttribute="trailing" secondItem="Ci4-fW-KjU" secondAttribute="trailing" id="zbx-Ch-NEt"/>
</constraints>
</customView>
@ -230,13 +220,14 @@
</view>
<connections>
<outlet property="defaultBrowserPopup" destination="Ci4-fW-KjU" id="7Nh-nU-Sbc"/>
<outlet property="defaultRSSReaderPopup" destination="cSu-T2-Jby" id="cih-nK-gdj"/>
<outlet property="defaultRSSReaderPopup" destination="48p-6Z-Yid" id="qwI-fd-LlN"/>
<outlet property="showUnreadCountCheckbox" destination="mwT-nY-TrX" id="ZH9-P5-JkT"/>
</connections>
</viewController>
<customObject id="bSQ-tq-wd3" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
<userDefaultsController representsSharedInstance="YES" id="mAF-gO-1PI"/>
</objects>
<point key="canvasLocation" x="-568.5" y="440.5"/>
<point key="canvasLocation" x="-570" y="395"/>
</scene>
<!--Advanced Preferences View Controller-->
<scene sceneID="z1G-rc-sP5">
@ -307,7 +298,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TKI-a9-bRX">
<rect key="frame" x="84" y="81" width="160" height="32"/>
<rect key="frame" x="84" y="81" width="148" height="32"/>
<buttonCell key="cell" type="push" title="Check for Updates" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="AaA-Rr-UYD">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -345,7 +336,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="uuc-f2-OFX">
<rect key="frame" x="84" y="-7" width="160" height="32"/>
<rect key="frame" x="84" y="-7" width="148" height="32"/>
<buttonCell key="cell" type="push" title="Privacy Policy" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="kSv-Wu-NYx">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
@ -416,16 +407,16 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="7UM-iq-OLB" customClass="PreferencesTableViewBackgroundView" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="44" width="160" height="223"/>
<rect key="frame" x="20" y="44" width="160" height="264"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PaF-du-r3c">
<rect key="frame" x="1" y="0.0" width="158" height="222"/>
<rect key="frame" x="1" y="0.0" width="158" height="263"/>
<clipView key="contentView" id="cil-Gq-akO">
<rect key="frame" x="0.0" y="0.0" width="158" height="222"/>
<rect key="frame" x="0.0" y="0.0" width="158" height="263"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" viewBased="YES" id="aTp-KR-y6b">
<rect key="frame" x="0.0" y="0.0" width="159" height="222"/>
<rect key="frame" x="0.0" y="0.0" width="188" height="263"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -444,7 +435,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="Cell" id="h2e-5a-qNO">
<rect key="frame" x="1" y="1" width="156" height="17"/>
<rect key="frame" x="11" y="1" width="165" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="27f-p8-Wnt">
@ -456,7 +447,7 @@
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="lKA-xK-bHU"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hR2-bm-0wE">
<rect key="frame" x="26" y="1" width="126" height="16"/>
<rect key="frame" x="26" y="1" width="135" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="CcS-BO-sdv">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -532,7 +523,7 @@
<rect key="frame" x="83" y="20" width="97" height="24"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Y7D-xQ-wep">
<rect key="frame" x="188" y="20" width="242" height="247"/>
<rect key="frame" x="188" y="20" width="242" height="288"/>
</customView>
</subviews>
<constraints>
@ -587,16 +578,16 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="pjs-G4-byk" customClass="PreferencesTableViewBackgroundView" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="44" width="160" height="216"/>
<rect key="frame" x="20" y="44" width="160" height="257"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="29T-r2-ckC">
<rect key="frame" x="1" y="0.0" width="158" height="215"/>
<rect key="frame" x="1" y="0.0" width="158" height="256"/>
<clipView key="contentView" id="dXw-GY-TP8">
<rect key="frame" x="0.0" y="0.0" width="158" height="215"/>
<rect key="frame" x="0.0" y="0.0" width="158" height="256"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" viewBased="YES" id="dfn-Vn-oDp">
<rect key="frame" x="0.0" y="0.0" width="159" height="215"/>
<rect key="frame" x="0.0" y="0.0" width="188" height="256"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -615,7 +606,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="Cell" id="xQs-6E-Kpy">
<rect key="frame" x="1" y="1" width="156" height="17"/>
<rect key="frame" x="11" y="1" width="165" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kmG-vw-CbN">
@ -627,7 +618,7 @@
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="OVD-Jo-TXU"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6cr-cB-qAN">
<rect key="frame" x="26" y="1" width="126" height="16"/>
<rect key="frame" x="26" y="1" width="135" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="goO-QG-kk7">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -699,7 +690,7 @@
<rect key="frame" x="83" y="20" width="97" height="24"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="N1N-pE-gBL">
<rect key="frame" x="188" y="20" width="242" height="240"/>
<rect key="frame" x="188" y="20" width="242" height="281"/>
</customView>
</subviews>
<constraints>

View File

@ -26,6 +26,7 @@ final class BuiltinSmartFeedInspectorViewController: NSViewController, Inspector
updateSmartFeed()
}
}
var windowTitle: String = NSLocalizedString("Smart Feed Inspector", comment: "Smart Feed Inspector window title")
func canInspect(_ objects: [Any]) -> Bool {
@ -62,5 +63,6 @@ private extension BuiltinSmartFeedInspectorViewController {
func updateUI() {
nameTextField?.stringValue = smartFeed?.nameForDisplay ?? ""
windowTitle = smartFeed?.nameForDisplay ?? NSLocalizedString("Smart Feed Inspector", comment: "Smart Feed Inspector window title")
}
}

View File

@ -30,6 +30,7 @@ final class FolderInspectorViewController: NSViewController, Inspector {
updateFolder()
}
}
var windowTitle: String = NSLocalizedString("Folder Inspector", comment: "Folder Inspector window title")
func canInspect(_ objects: [Any]) -> Bool {
@ -103,5 +104,6 @@ private extension FolderInspectorViewController {
if nameTextField.stringValue != name {
nameTextField.stringValue = name
}
windowTitle = folder?.nameForDisplay ?? NSLocalizedString("Folder Inspector", comment: "Folder Inspector window title")
}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="cfG-Pn-VJS">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="cfG-Pn-VJS">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15505"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097.2"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -33,11 +33,11 @@
<objects>
<viewController title="Feed" storyboardIdentifier="Feed" showSeguePresentationStyle="single" id="sfH-oR-GXm" customClass="WebFeedInspectorViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="ecA-UY-KEd">
<rect key="frame" x="0.0" y="0.0" width="256" height="332"/>
<rect key="frame" x="0.0" y="0.0" width="267" height="318"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="IWu-80-XC5">
<rect key="frame" x="20" y="200" width="216" height="56"/>
<rect key="frame" x="20" y="186" width="227" height="56"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="56" id="zV3-AX-gyC"/>
</constraints>
@ -54,7 +54,7 @@ Field</string>
</connections>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2WO-Iu-p5e">
<rect key="frame" x="18" y="96" width="220" height="16"/>
<rect key="frame" x="18" y="96" width="231" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Home Page" usesSingleLineMode="YES" id="Fg8-rA-G5J">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -62,7 +62,7 @@ Field</string>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="1000" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zm0-15-BFy">
<rect key="frame" x="18" y="76" width="220" height="16"/>
<rect key="frame" x="18" y="76" width="231" height="16"/>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="http://example.com/" id="L2p-ur-j7a">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -70,7 +70,7 @@ Field</string>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ju6-Zo-8X4">
<rect key="frame" x="18" y="40" width="220" height="16"/>
<rect key="frame" x="18" y="40" width="231" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" allowsUndo="NO" sendsActionOnEndEditing="YES" title="Feed" usesSingleLineMode="YES" id="zzB-rX-1dK">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -78,26 +78,16 @@ Field</string>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="1000" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Vvk-KG-JlG">
<rect key="frame" x="18" y="20" width="220" height="16"/>
<rect key="frame" x="18" y="20" width="231" height="16"/>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" sendsActionOnEndEditing="YES" title="http://example.com/feed" id="HpC-rK-YGK">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nH2-ab-KJ5">
<rect key="frame" x="18" y="130" width="180" height="18"/>
<buttonCell key="cell" type="check" title="Always Show Reader View" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="aRe-yV-R0h">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="isReaderViewAlwaysOnChanged:" target="sfH-oR-GXm" id="rsD-0e-ksP"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ZBX-E8-k9c">
<rect key="frame" x="18" y="164" width="179" height="18"/>
<buttonCell key="cell" type="check" title="Notify About New Articles" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Bw5-c7-yDX">
<button horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="ZBX-E8-k9c">
<rect key="frame" x="18" y="150" width="231" height="18"/>
<buttonCell key="cell" type="check" title="Show notifications for new articles" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Bw5-c7-yDX">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
@ -105,8 +95,18 @@ Field</string>
<action selector="isNotifyAboutNewArticlesChanged:" target="sfH-oR-GXm" id="Vx9-pQ-RnP"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nH2-ab-KJ5">
<rect key="frame" x="18" y="130" width="167" height="18"/>
<buttonCell key="cell" type="check" title="Always use Reader view" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="aRe-yV-R0h">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="isReaderViewAlwaysOnChanged:" target="sfH-oR-GXm" id="rsD-0e-ksP"/>
</connections>
</button>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="I6k-QR-VmV" customClass="IconView" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="104" y="264" width="48" height="48"/>
<rect key="frame" x="110" y="250" width="48" height="48"/>
<constraints>
<constraint firstAttribute="height" constant="48" id="Faa-nE-lkA"/>
<constraint firstAttribute="width" constant="48" id="esD-dT-oWU"/>
@ -118,7 +118,7 @@ Field</string>
<constraint firstItem="ZBX-E8-k9c" firstAttribute="top" secondItem="IWu-80-XC5" secondAttribute="bottom" constant="20" id="5L7-aZ-vdg"/>
<constraint firstItem="nH2-ab-KJ5" firstAttribute="leading" secondItem="ecA-UY-KEd" secondAttribute="leading" constant="20" symbolic="YES" id="8pK-lW-xQk"/>
<constraint firstItem="IWu-80-XC5" firstAttribute="top" secondItem="I6k-QR-VmV" secondAttribute="bottom" constant="8" symbolic="YES" id="Bea-j0-QMb"/>
<constraint firstItem="nH2-ab-KJ5" firstAttribute="top" secondItem="ZBX-E8-k9c" secondAttribute="bottom" constant="20" id="CpA-X9-EbP"/>
<constraint firstItem="nH2-ab-KJ5" firstAttribute="top" secondItem="ZBX-E8-k9c" secondAttribute="bottom" constant="6" symbolic="YES" id="CpA-X9-EbP"/>
<constraint firstAttribute="bottom" secondItem="Vvk-KG-JlG" secondAttribute="bottom" constant="20" id="IxJ-5N-NhL"/>
<constraint firstAttribute="trailing" secondItem="ju6-Zo-8X4" secondAttribute="trailing" constant="20" symbolic="YES" id="Jzi-tP-TIw"/>
<constraint firstAttribute="trailing" secondItem="Vvk-KG-JlG" secondAttribute="trailing" constant="20" symbolic="YES" id="KAS-A7-TxB"/>
@ -132,6 +132,7 @@ Field</string>
<constraint firstAttribute="trailing" secondItem="2WO-Iu-p5e" secondAttribute="trailing" constant="20" symbolic="YES" id="dLU-a6-nfx"/>
<constraint firstItem="I6k-QR-VmV" firstAttribute="centerX" secondItem="ecA-UY-KEd" secondAttribute="centerX" id="gFG-ZY-eNp"/>
<constraint firstAttribute="trailing" secondItem="zm0-15-BFy" secondAttribute="trailing" constant="20" symbolic="YES" id="js6-b2-FIR"/>
<constraint firstAttribute="trailing" secondItem="ZBX-E8-k9c" secondAttribute="trailing" constant="20" symbolic="YES" id="nOw-cs-o8e"/>
<constraint firstItem="IWu-80-XC5" firstAttribute="leading" secondItem="ecA-UY-KEd" secondAttribute="leading" constant="20" symbolic="YES" id="r6h-Z0-g7b"/>
<constraint firstItem="2WO-Iu-p5e" firstAttribute="top" secondItem="nH2-ab-KJ5" secondAttribute="bottom" constant="20" id="rRv-qO-dPa"/>
<constraint firstItem="Vvk-KG-JlG" firstAttribute="top" secondItem="ju6-Zo-8X4" secondAttribute="bottom" constant="4" id="sAt-dN-Taz"/>
@ -156,11 +157,11 @@ Field</string>
<objects>
<viewController title="Folder" storyboardIdentifier="Folder" showSeguePresentationStyle="single" id="ylq-Dz-pnT" customClass="FolderInspectorViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="DIQ-fD-5q6">
<rect key="frame" x="0.0" y="0.0" width="256" height="152"/>
<rect key="frame" x="0.0" y="0.0" width="267" height="152"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="HJF-Gi-62u">
<rect key="frame" x="104" y="84" width="48" height="48"/>
<rect key="frame" x="110" y="84" width="48" height="48"/>
<constraints>
<constraint firstAttribute="width" constant="48" id="dts-Bk-DzJ"/>
<constraint firstAttribute="height" constant="48" id="vtI-1q-OKy"/>
@ -168,7 +169,7 @@ Field</string>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="NSFolder" id="C4n-vS-297"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="jHf-rc-GNr" userLabel="Folder Name Field">
<rect key="frame" x="20" y="20" width="216" height="56"/>
<rect key="frame" x="20" y="20" width="227" height="56"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="56" id="Ele-JD-mB2"/>
</constraints>
@ -207,11 +208,11 @@ Field</string>
<objects>
<viewController title="Builtin Smart Feed" storyboardIdentifier="BuiltinSmartFeed" showSeguePresentationStyle="single" id="ye3-co-8lc" customClass="BuiltinSmartFeedInspectorViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="DXo-3M-jJQ">
<rect key="frame" x="0.0" y="0.0" width="256" height="112"/>
<rect key="frame" x="0.0" y="0.0" width="267" height="112"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="cwK-Ep-mNL">
<rect key="frame" x="104" y="44" width="48" height="48"/>
<rect key="frame" x="110" y="44" width="48" height="48"/>
<constraints>
<constraint firstAttribute="width" constant="48" id="33r-tp-RWH"/>
<constraint firstAttribute="height" constant="48" id="8F1-sH-5Xs"/>
@ -219,7 +220,7 @@ Field</string>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="NSSmartBadgeTemplate" id="Z52-bd-Lgz"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4Xp-FX-kn3">
<rect key="frame" x="18" y="20" width="220" height="16"/>
<rect key="frame" x="18" y="20" width="231" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Label" id="3v9-Z7-d7l">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@ -249,11 +250,11 @@ Field</string>
<objects>
<viewController title="Nothing to inspect" storyboardIdentifier="Nothing" showSeguePresentationStyle="single" id="Fdj-2F-Kl1" customClass="NothingInspectorViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="FDE-PJ-bJl">
<rect key="frame" x="0.0" y="0.0" width="256" height="56"/>
<rect key="frame" x="0.0" y="0.0" width="267" height="56"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="icb-M6-R2N">
<rect key="frame" x="18" y="20" width="220" height="16"/>
<rect key="frame" x="18" y="20" width="231" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Nothing to inspect" id="iLD-8q-EAJ">
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
@ -261,7 +262,7 @@ Field</string>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zQp-oc-Qtc">
<rect key="frame" x="18" y="20" width="220" height="16"/>
<rect key="frame" x="18" y="20" width="231" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="center" title="Multiple selection" id="5oG-0x-T8O">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>

View File

@ -12,6 +12,7 @@ protocol Inspector: class {
var objects: [Any]? { get set }
var isFallbackInspector: Bool { get } // Can handle nothing-to-inspect or unexpected type of objects.
var windowTitle: String { get }
func canInspect(_ objects: [Any]) -> Bool
}
@ -62,6 +63,7 @@ final class InspectorWindowController: NSWindowController {
inspectors = [feedInspector, folderInspector, builtinSmartFeedInspector, nothingInspector]
currentInspector = nothingInspector
window?.title = currentInspector.windowTitle
if let savedOrigin = originFromDefaults() {
window?.setFlippedOriginAdjustingForScreen(savedOrigin)
@ -108,7 +110,11 @@ private extension InspectorWindowController {
guard let window = window else {
return
}
DispatchQueue.main.async {
window.title = inspector.windowTitle
}
let flippedOrigin = window.flippedOrigin
if window.contentViewController != inspector {

View File

@ -19,6 +19,7 @@ final class NothingInspectorViewController: NSViewController, Inspector {
updateTextFields()
}
}
var windowTitle: String = NSLocalizedString("Inspector", comment: "Inspector window title")
func canInspect(_ objects: [Any]) -> Bool {

View File

@ -38,6 +38,7 @@ final class WebFeedInspectorViewController: NSViewController, Inspector {
updateFeed()
}
}
var windowTitle: String = NSLocalizedString("Feed Inspector", comment: "Feed Inspector window title")
func canInspect(_ objects: [Any]) -> Bool {
return objects.count == 1 && objects.first is WebFeed
@ -112,6 +113,7 @@ extension WebFeedInspectorViewController: NSTextFieldDelegate {
return
}
feed.editedName = nameTextField.stringValue
windowTitle = feed.editedName ?? NSLocalizedString("Feed Inspector", comment: "Feed Inspector window title")
}
}
@ -133,7 +135,7 @@ private extension WebFeedInspectorViewController {
updateFeedURL()
updateNotifyAboutNewArticles()
updateIsReaderViewAlwaysOn()
windowTitle = feed?.nameForDisplay ?? NSLocalizedString("Feed Inspector", comment: "Feed Inspector window title")
view.needsLayout = true
}
@ -202,7 +204,7 @@ private extension WebFeedInspectorViewController {
updateAlert.addButton(withTitle: NSLocalizedString("Close", comment: "Close"))
let modalResponse = updateAlert.runModal()
if modalResponse == .alertFirstButtonReturn {
NSWorkspace.shared.open(URL(fileURLWithPath: "x-apple.systempreferences:com.apple.preference.notifications"))
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.notifications")!)
}
}

View File

@ -10,8 +10,8 @@ body {
:root {
--body-color: #444;
--body-background-color: -apple-system-text-background;
--accent-color: rgb(8, 106, 238, 1);
--block-quote-border-color: rgb(8, 106, 238, .50);
--accent-color: rgba(8, 106, 238, 1);
--block-quote-border-color: rgba(8, 106, 238, .50);
}
@media(prefers-color-scheme: dark) {

View File

@ -74,7 +74,7 @@ class LegacyArticleExtractorButton: NSButton {
private func makeLayerForImage(_ image: NSImage) -> CALayer {
let imageLayer = CALayer()
imageLayer.bounds = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
imageLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
imageLayer.position = CGPoint(x: bounds.midX, y: floor(bounds.midY))
return imageLayer
}
@ -90,17 +90,17 @@ class LegacyArticleExtractorButton: NSButton {
let imageProgress2 = AppAssets.legacyArticleExtractorProgress2
let imageProgress3 = AppAssets.legacyArticleExtractorProgress3
let imageProgress4 = AppAssets.legacyArticleExtractorProgress4
let images = [imageProgress1, imageProgress2, imageProgress3, imageProgress4, imageProgress3, imageProgress2]
let images = [imageProgress1, imageProgress2, imageProgress3, imageProgress4, imageProgress3, imageProgress2, imageProgress1]
let imageLayer = CALayer()
imageLayer.bounds = CGRect(x: 0, y: 0, width: imageProgress1?.size.width ?? 0, height: imageProgress1?.size.height ?? 0)
imageLayer.position = CGPoint(x: bounds.midX, y: bounds.midY)
imageLayer.position = CGPoint(x: bounds.midX, y: floor(bounds.midY))
hostedLayer.addSublayer(imageLayer)
let animation = CAKeyframeAnimation(keyPath: "contents")
animation.calculationMode = CAAnimationCalculationMode.linear
animation.keyTimes = [0, 0.2, 0.4, 0.6, 0.8, 1]
animation.keyTimes = [0, 0.16, 0.32, 0.50, 0.66, 0.82, 1]
animation.duration = 2
animation.values = images as [Any]
animation.repeatCount = HUGE

View File

@ -295,6 +295,9 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
}
@IBAction func openInBrowser(_ sender: Any?) {
if AppDefaults.shared.openInBrowserInBackground {
window?.makeKeyAndOrderFront(self)
}
openArticleInBrowser(sender)
}
@ -304,6 +307,9 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
}
@IBAction func openInBrowserUsingOppositeOfSettings(_ sender: Any?) {
if !AppDefaults.shared.openInBrowserInBackground {
window?.makeKeyAndOrderFront(self)
}
if let link = currentLink {
Browser.open(link, inBackground: !AppDefaults.shared.openInBrowserInBackground)
}
@ -413,7 +419,11 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
sidebarViewController?.focus()
}
}
@IBAction func markOlderArticlesAsRead(_ sender: Any?) {
currentTimelineViewController?.markOlderArticlesRead()
}
@IBAction func markAboveArticlesAsRead(_ sender: Any?) {
currentTimelineViewController?.markAboveArticlesRead()
}

View File

@ -48,10 +48,10 @@ struct TimelineCellAppearance: Equatable {
let smallItemFontSize = floor(actualFontSize * 0.90)
let largeItemFontSize = actualFontSize
self.feedNameFont = NSFont.systemFont(ofSize: smallItemFontSize)
self.feedNameFont = NSFont.systemFont(ofSize: smallItemFontSize, weight: NSFont.Weight.bold)
self.dateFont = NSFont.systemFont(ofSize: smallItemFontSize, weight: NSFont.Weight.bold)
self.titleFont = NSFont.systemFont(ofSize: largeItemFontSize, weight: NSFont.Weight.semibold)
self.textFont = NSFont.systemFont(ofSize: largeItemFontSize, weight: NSFont.Weight.light)
self.textFont = NSFont.systemFont(ofSize: largeItemFontSize)
self.textOnlyFont = NSFont.systemFont(ofSize: largeItemFontSize)
self.showIcon = showIcon

View File

@ -34,6 +34,21 @@ class TimelineTableRowView : NSTableRowView {
super.init(coder: coder)
}
override func drawBackground(in dirtyRect: NSRect) {
NSColor.controlBackgroundColor.setFill()
dirtyRect.fill()
}
override func drawSelection(in dirtyRect: NSRect) {
if isEmphasized {
NSColor.selectedContentBackgroundColor.setFill()
dirtyRect.fill()
} else {
NSColor.unemphasizedSelectedContentBackgroundColor.setFill()
dirtyRect.fill()
}
}
private var cellView: TimelineTableCellView? {
for oneSubview in subviews {
if let foundView = oneSubview as? TimelineTableCellView {

View File

@ -44,11 +44,20 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
}
var isCleanUpAvailable: Bool {
guard isReadFiltered ?? false else { return false }
let isEligibleForCleanUp: Bool?
if representedObjects?.count == 1, let timelineFeed = representedObjects?.first as? Feed, timelineFeed.defaultReadFilterType == .alwaysRead {
isEligibleForCleanUp = true
} else {
isEligibleForCleanUp = isReadFiltered
}
guard isEligibleForCleanUp ?? false else { return false }
let readSelectedCount = selectedArticles.filter({ $0.status.read }).count
let readArticleCount = articles.count - unreadCount
let availableToCleanCount = readArticleCount - readSelectedCount
return availableToCleanCount > 0
}
@ -451,6 +460,10 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
return .canDoNothing
}
func markOlderArticlesRead() {
markOlderArticlesRead(selectedArticles)
}
func markAboveArticlesRead() {
markAboveArticlesRead(selectedArticles)
}
@ -469,6 +482,33 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
return articles.articlesBelow(article: last).canMarkAllAsRead()
}
func markOlderArticlesRead(_ selectedArticles: [Article]) {
// Mark articles older than the selectedArticles(s) as read.
var cutoffDate: Date? = nil
for article in selectedArticles {
if cutoffDate == nil {
cutoffDate = article.logicalDatePublished
}
else if cutoffDate! > article.logicalDatePublished {
cutoffDate = article.logicalDatePublished
}
}
if cutoffDate == nil {
return
}
let articlesToMark = articles.filter { $0.logicalDatePublished < cutoffDate! }
if articlesToMark.isEmpty {
return
}
guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: articlesToMark, markingRead: true, undoManager: undoManager) else {
return
}
runCommand(markReadCommand)
}
func markAboveArticlesRead(_ selectedArticles: [Article]) {
guard let first = selectedArticles.first else { return }
let articlesToMark = articles.articlesAbove(article: first)
@ -1129,19 +1169,19 @@ private extension TimelineViewController {
func fetchUnsortedArticlesSync(for representedObjects: [Any]) -> Set<Article> {
cancelPendingAsyncFetches()
let articleFetchers = representedObjects.compactMap{ $0 as? ArticleFetcher }
if articleFetchers.isEmpty {
let fetchers = representedObjects.compactMap{ $0 as? ArticleFetcher }
if fetchers.isEmpty {
return Set<Article>()
}
var fetchedArticles = Set<Article>()
for articleFetcher in articleFetchers {
if isReadFiltered ?? true {
if let articles = try? articleFetcher.fetchUnreadArticles() {
for fetchers in fetchers {
if (fetchers as? Feed)?.readFiltered(readFilterEnabledTable: readFilterEnabledTable) ?? true {
if let articles = try? fetchers.fetchUnreadArticles() {
fetchedArticles.formUnion(articles)
}
} else {
if let articles = try? articleFetcher.fetchArticles() {
if let articles = try? fetchers.fetchArticles() {
fetchedArticles.formUnion(articles)
}
}
@ -1154,7 +1194,8 @@ private extension TimelineViewController {
// if its been superseded by a newer fetch, or the timeline was emptied, etc., it wont get called.
precondition(Thread.isMainThread)
cancelPendingAsyncFetches()
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, readFilter: isReadFiltered ?? true, representedObjects: representedObjects) { [weak self] (articles, operation) in
let fetchers = representedObjects.compactMap { $0 as? ArticleFetcher }
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, readFilterEnabledTable: readFilterEnabledTable, fetchers: fetchers) { [weak self] (articles, operation) in
precondition(Thread.isMainThread)
guard !operation.isCanceled, let strongSelf = self, operation.id == strongSelf.fetchSerialNumber else {
return

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097.3"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -13,19 +13,19 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<customView misplaced="YES" id="c22-O7-iKe">
<rect key="frame" x="0.0" y="0.0" width="480" height="292"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<scrollView autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aV5-XD-qtI">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<rect key="frame" x="0.0" y="0.0" width="480" height="292"/>
<clipView key="contentView" id="UDd-jz-Pwe">
<rect key="frame" x="1" y="1" width="478" height="270"/>
<rect key="frame" x="1" y="1" width="478" height="290"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="40" rowSizeStyle="automatic" viewBased="YES" id="YWY-HH-lRy">
<rect key="frame" x="0.0" y="0.0" width="478" height="270"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="frame" x="0.0" y="0.0" width="478" height="290"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
@ -33,7 +33,6 @@
<tableColumns>
<tableColumn width="475" minWidth="40" maxWidth="1000" id="aZS-IU-bl6">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
@ -49,7 +48,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="17" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xsr-pQ-ts2">
<rect key="frame" x="151" y="8" width="173" height="24"/>
<rect key="frame" x="20" y="8" width="174" height="24"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="cjk-vg-Vn6">
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
@ -60,7 +59,7 @@
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="ros-80-3xn"/>
</imageView>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="5ut-F4-bRA">
<rect key="frame" x="39" y="0.0" width="136" height="24"/>
<rect key="frame" x="39" y="0.0" width="137" height="24"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="2mk-0x-ly6">
<font key="font" metaFont="system" size="20"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
@ -77,10 +76,24 @@
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<button translatesAutoresizingMaskIntoConstraints="NO" id="mDB-Dl-30S">
<rect key="frame" x="0.0" y="0.0" width="475" height="40"/>
<buttonCell key="cell" type="bevel" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="yTN-9d-fp3">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="pressed:" target="wVK-qI-WAx" id="fXc-TU-jxw"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="mDB-Dl-30S" firstAttribute="top" secondItem="wVK-qI-WAx" secondAttribute="top" id="5Q5-w5-FHX"/>
<constraint firstAttribute="trailing" secondItem="mDB-Dl-30S" secondAttribute="trailing" id="ZXk-x2-YMq"/>
<constraint firstItem="mDB-Dl-30S" firstAttribute="leading" secondItem="wVK-qI-WAx" secondAttribute="leading" id="hBc-Ju-z9p"/>
<constraint firstItem="xsr-pQ-ts2" firstAttribute="centerY" secondItem="wVK-qI-WAx" secondAttribute="centerY" id="lQw-mm-Vnb"/>
<constraint firstItem="xsr-pQ-ts2" firstAttribute="leading" secondItem="wVK-qI-WAx" secondAttribute="leading" constant="20" id="msT-9I-cEP"/>
<constraint firstAttribute="bottom" secondItem="mDB-Dl-30S" secondAttribute="bottom" id="vfZ-QH-wrN"/>
</constraints>
<connections>
<outlet property="accountImageView" destination="cjk-vg-Vn6" id="laA-LX-gYz"/>
@ -109,6 +122,7 @@
<constraint firstItem="aV5-XD-qtI" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" id="hrT-XK-tRk"/>
<constraint firstItem="aV5-XD-qtI" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" id="oGe-X8-oCz"/>
</constraints>
<point key="canvasLocation" x="139" y="154"/>
</customView>
</objects>
</document>

View File

@ -7,10 +7,23 @@
//
import AppKit
import Account
protocol AccountsAddTableCellViewDelegate: class {
func addAccount(_ accountType: AccountType)
}
class AccountsAddTableCellView: NSTableCellView {
weak var delegate: AccountsAddTableCellViewDelegate?
var accountType: AccountType?
@IBOutlet weak var accountImageView: NSImageView?
@IBOutlet weak var accountNameLabel: NSTextField?
@IBAction func pressed(_ sender: Any) {
guard let accountType = accountType else { return }
delegate?.addAccount(accountType)
}
}

View File

@ -61,6 +61,10 @@ extension AccountsAddViewController: NSTableViewDelegate {
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? AccountsAddTableCellView {
cell.accountType = addableAccountTypes[row]
cell.delegate = self
switch addableAccountTypes[row] {
case .onMyMac:
cell.accountNameLabel?.stringValue = Account.defaultLocalAccountName
@ -88,19 +92,21 @@ extension AccountsAddViewController: NSTableViewDelegate {
}
return nil
}
func tableViewSelectionDidChange(_ notification: Notification) {
let selectedRow = tableView.selectedRow
guard selectedRow != -1 else {
return
}
}
switch addableAccountTypes[selectedRow] {
// MARK: AccountsAddTableCellViewDelegate
extension AccountsAddViewController: AccountsAddTableCellViewDelegate {
func addAccount(_ accountType: AccountType) {
switch accountType {
case .onMyMac:
let accountsAddLocalWindowController = AccountsAddLocalWindowController()
accountsAddLocalWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsAddLocalWindowController
case .cloudKit:
let accountsAddCloudKitWindowController = AccountsAddCloudKitWindowController()
accountsAddCloudKitWindowController.runSheetOnWindow(self.view.window!) { response in
@ -110,34 +116,66 @@ extension AccountsAddViewController: NSTableViewDelegate {
}
}
accountsAddWindowController = accountsAddCloudKitWindowController
case .feedbin:
let accountsFeedbinWindowController = AccountsFeedbinWindowController()
accountsFeedbinWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsFeedbinWindowController
case .feedWrangler:
let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController()
accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsFeedWranglerWindowController
case .freshRSS:
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
accountsReaderAPIWindowController.accountType = .freshRSS
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsReaderAPIWindowController
case .feedly:
let addAccount = OAuthAccountAuthorizationOperation(accountType: .feedly)
addAccount.delegate = self
addAccount.presentationAnchor = self.view.window!
runAwaitingFeedlyLoginAlertModal(forLifetimeOf: addAccount)
MainThreadOperationQueue.shared.add(addAccount)
case .newsBlur:
let accountsNewsBlurWindowController = AccountsNewsBlurWindowController()
accountsNewsBlurWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsNewsBlurWindowController
}
tableView.selectRowIndexes([], byExtendingSelection: false)
}
private func runAwaitingFeedlyLoginAlertModal(forLifetimeOf operation: OAuthAccountAuthorizationOperation) {
let alert = NSAlert()
alert.alertStyle = .informational
alert.messageText = NSLocalizedString("Waiting for access to Feedly",
comment: "Alert title when adding a Feedly account and waiting for authorization from the user.")
alert.informativeText = NSLocalizedString("Your default web browser will open the Feedly login for you to authorize access.",
comment: "Alert informative text when adding a Feedly account and waiting for authorization from the user.")
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel"))
let attachedWindow = self.view.window!
alert.beginSheetModal(for: attachedWindow) { response in
if response == .alertFirstButtonReturn {
operation.cancel()
}
}
operation.completionBlock = { _ in
guard alert.window.isVisible else {
return
}
attachedWindow.endSheet(alert.window)
}
}
}
// MARK: OAuthAccountAuthorizationOperationDelegate
@ -145,6 +183,12 @@ extension AccountsAddViewController: NSTableViewDelegate {
extension AccountsAddViewController: OAuthAccountAuthorizationOperationDelegate {
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) {
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
// When this authorization is granted, the browser remains the foreground app which is unfortunate
// because the user probably wants to see the result of authorizing NetNewsWire to act on their behalf.
NSApp.activate(ignoringOtherApps: true)
account.refreshAll { [weak self] result in
switch result {
case .success:
@ -156,6 +200,10 @@ extension AccountsAddViewController: OAuthAccountAuthorizationOperationDelegate
}
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) {
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
NSApp.activate(ignoringOtherApps: true)
view.window?.presentError(error)
}
}

View File

@ -56,6 +56,11 @@ class AccountsFeedWranglerWindowController: NSWindowController {
return
}
guard !AccountManager.shared.duplicateServiceAccount(type: .feedWrangler, username: usernameTextField.stringValue) else {
self.errorMessageLabel.stringValue = NSLocalizedString("There is already a FeedWrangler account with that username created.", comment: "Duplicate Error")
return
}
actionButton.isEnabled = false
progressIndicator.isHidden = false
progressIndicator.startAnimation(self)

View File

@ -58,6 +58,11 @@ class AccountsFeedbinWindowController: NSWindowController {
return
}
guard !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: usernameTextField.stringValue) else {
self.errorMessageLabel.stringValue = NSLocalizedString("There is already a Feedbin account with that username created.", comment: "Duplicate Error")
return
}
actionButton.isEnabled = false
progressIndicator.isHidden = false
progressIndicator.startAnimation(self)

View File

@ -55,7 +55,12 @@ class AccountsNewsBlurWindowController: NSWindowController {
self.errorMessageLabel.stringValue = NSLocalizedString("Username required.", comment: "Credentials Error")
return
}
guard !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: usernameTextField.stringValue) else {
self.errorMessageLabel.stringValue = NSLocalizedString("There is already a NewsBlur account with that username created.", comment: "Duplicate Error")
return
}
actionButton.isEnabled = false
progressIndicator.isHidden = false
progressIndicator.startAnimation(self)

View File

@ -9,11 +9,15 @@
import AppKit
import RSCore
import RSWeb
import UserNotifications
final class GeneralPreferencesViewController: NSViewController {
private var userNotificationSettings: UNNotificationSettings?
@IBOutlet var defaultRSSReaderPopup: NSPopUpButton!
@IBOutlet var defaultBrowserPopup: NSPopUpButton!
@IBOutlet weak var showUnreadCountCheckbox: NSButton!
private var rssReaderInfo = RSSReaderInfo()
public override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
@ -29,6 +33,7 @@ final class GeneralPreferencesViewController: NSViewController {
override func viewWillAppear() {
super.viewWillAppear()
updateUI()
updateNotificationSettings()
}
// MARK: - Notifications
@ -58,6 +63,47 @@ final class GeneralPreferencesViewController: NSViewController {
AppDefaults.shared.defaultBrowserID = bundleID
updateUI()
}
@IBAction func toggleShowingUnreadCount(_ sender: Any) {
guard let checkbox = sender as? NSButton else { return }
guard userNotificationSettings != nil else {
DispatchQueue.main.async {
self.showUnreadCountCheckbox.setNextState()
}
return
}
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
self.updateNotificationSettings()
if settings.authorizationStatus == .denied {
DispatchQueue.main.async {
self.showUnreadCountCheckbox.setNextState()
self.showNotificationsDeniedError()
}
} else if settings.authorizationStatus == .authorized {
DispatchQueue.main.async {
AppDefaults.shared.hideDockUnreadCount = (checkbox.state.rawValue == 0)
}
} else {
UNUserNotificationCenter.current().requestAuthorization(options: [.badge]) { (granted, error) in
self.updateNotificationSettings()
if granted {
DispatchQueue.main.async {
AppDefaults.shared.hideDockUnreadCount = checkbox.state.rawValue == 0
NSApplication.shared.registerForRemoteNotifications()
}
} else {
DispatchQueue.main.async {
self.showUnreadCountCheckbox.setNextState()
}
}
}
}
}
}
}
// MARK: - Private
@ -70,8 +116,9 @@ private extension GeneralPreferencesViewController {
func updateUI() {
rssReaderInfo = RSSReaderInfo()
updateRSSReaderPopup()
updateBrowserPopup()
updateRSSReaderPopup()
updateHideUnreadCountCheckbox()
}
func updateRSSReaderPopup() {
@ -166,8 +213,36 @@ private extension GeneralPreferencesViewController {
defaultBrowserPopup.selectItem(at: defaultBrowserPopup.indexOfItem(withRepresentedObject: AppDefaults.shared.defaultBrowserID))
}
}
func updateHideUnreadCountCheckbox() {
showUnreadCountCheckbox.state = AppDefaults.shared.hideDockUnreadCount ? .off : .on
}
func updateNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
self.userNotificationSettings = settings
if settings.authorizationStatus == .authorized {
DispatchQueue.main.async {
NSApplication.shared.registerForRemoteNotifications()
}
}
}
}
func showNotificationsDeniedError() {
let updateAlert = NSAlert()
updateAlert.alertStyle = .informational
updateAlert.messageText = NSLocalizedString("Enable Notifications", comment: "Notifications")
updateAlert.informativeText = NSLocalizedString("To enable notifications, open Notifications in System Preferences, then find NetNewsWire in the list.", comment: "To enable notifications, open Notifications in System Preferences, then find NetNewsWire in the list.")
updateAlert.addButton(withTitle: NSLocalizedString("Open System Preferences", comment: "Open System Preferences"))
updateAlert.addButton(withTitle: NSLocalizedString("Close", comment: "Close"))
let modalResponse = updateAlert.runModal()
if modalResponse == .alertFirstButtonReturn {
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.notifications")!)
}
}
}
// MARK: - RSSReaderInfo

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

View File

@ -1,12 +1,22 @@
{
"images" : [
{
"filename" : "ArticleExtractor.png",
"idiom" : "universal",
"filename" : "ArticleExtractor.pdf"
"scale" : "1x"
},
{
"filename" : "ArticleExtractor@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

View File

@ -1,12 +1,22 @@
{
"images" : [
{
"filename" : "ArticleExtractorInactiveDark.png",
"idiom" : "universal",
"filename" : "ArticleExtractorInactiveDark.pdf"
"scale" : "1x"
},
{
"filename" : "ArticleExtractorInactiveDark@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

View File

@ -1,12 +1,22 @@
{
"images" : [
{
"filename" : "ArticleExtractorInactiveLight.png",
"idiom" : "universal",
"filename" : "ArticleExtractorInactiveLight.pdf"
"scale" : "1x"
},
{
"filename" : "ArticleExtractorInactiveLight@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

View File

@ -1,12 +1,22 @@
{
"images" : [
{
"filename" : "ArticleExtractorProgress1.png",
"idiom" : "universal",
"filename" : "ArticleExtractorProgress1.pdf"
"scale" : "1x"
},
{
"filename" : "ArticleExtractorProgress1@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 B

View File

@ -1,12 +1,22 @@
{
"images" : [
{
"filename" : "ArticleExtractorProgress2.png",
"idiom" : "universal",
"filename" : "ArticleExtractorProgress2.pdf"
"scale" : "1x"
},
{
"filename" : "ArticleExtractorProgress2@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 B

View File

@ -1,12 +1,22 @@
{
"images" : [
{
"filename" : "ArticleExtractorProgress3.png",
"idiom" : "universal",
"filename" : "ArticleExtractorProgress3.pdf"
"scale" : "1x"
},
{
"filename" : "ArticleExtractorProgress3@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

View File

@ -1,12 +1,22 @@
{
"images" : [
{
"filename" : "ArticleExtractorProgress4.png",
"idiom" : "universal",
"filename" : "ArticleExtractorProgress4.pdf"
"scale" : "1x"
},
{
"filename" : "ArticleExtractorProgress4@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View File

@ -1,4 +1,4 @@
{\rtf1\ansi\ansicpg1252\cocoartf2511
{\rtf1\ansi\ansicpg1252\cocoartf2513
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 LucidaGrande-Bold;\f1\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}
@ -18,10 +18,13 @@ By Brent Simmons\
\f0\b \cf2 Credits:
\f1\b0 \
\pard\pardeftab720\li360\sa60\partightenfactor0
\cf2 App icon and most other icons: {\field{\*\fldinst{HYPERLINK "https://twitter.com/BradEllis"}}{\fldrslt Brad Ellis}}\
Major code contributors: {\field{\*\fldinst{HYPERLINK "https://github.com/olofhellman"}}{\fldrslt Olof Hellman}}, {\field{\*\fldinst{HYPERLINK "https://github.com/vincode-io"}}{\fldrslt Maurice Parker}}, and {\field{\*\fldinst{HYPERLINK "https://github.com/danielpunkass"}}{\fldrslt Daniel Jalkut\
}}Help book by {\field{\*\fldinst{HYPERLINK "https://nostodnayr.net/"}}{\fldrslt Ryan Dotson}}\
Difficult infrastructure by {\field{\*\fldinst{HYPERLINK "https://rhonabwy.com/"}}{\fldrslt Joe Heck}}\
\cf2 Lead developer: {\field{\*\fldinst{HYPERLINK "https://github.com/vincode-io"}}{\fldrslt Maurice Parker}}\
App icon and most other icons: {\field{\*\fldinst{HYPERLINK "https://twitter.com/BradEllis"}}{\fldrslt Brad Ellis}}\
Feedly syncing: {\field{\*\fldinst{HYPERLINK "https://twitter.com/kielgillard"}}{\fldrslt Kiel Gillard}}\
Under-the-hood magic and CSS stylin\'92s: {\field{\*\fldinst{HYPERLINK "https://github.com/wevah"}}{\fldrslt Nate Weaver}}\
Newsfoot (JS footnote displayer): {\field{\*\fldinst{HYPERLINK "https://github.com/brehaut/"}}{\fldrslt Andrew Brehaut}}\
Help book: {\field{\*\fldinst{HYPERLINK "https://nostodnayr.net/"}}{\fldrslt Ryan Dotson}}\
And featuring contributions from {\field{\*\fldinst{HYPERLINK "https://github.com/danielpunkass"}}{\fldrslt Daniel Jalkut}}, {\field{\*\fldinst{HYPERLINK "https://rhonabwy.com/"}}{\fldrslt Joe Heck}}, {\field{\*\fldinst{HYPERLINK "https://github.com/olofhellman"}}{\fldrslt Olof Hellman}}, {\field{\*\fldinst{HYPERLINK "https://blog.rizwan.dev/"}}{\fldrslt Rizwan Mohamed Ibrahim}}, {\field{\*\fldinst{HYPERLINK "https://stuartbreckenridge.com/"}}{\fldrslt Stuart Breckenridge}}, {\field{\*\fldinst{HYPERLINK "https://twitter.com/philviso"}}{\fldrslt Phil Viso}}, and {\field{\*\fldinst{HYPERLINK "https://github.com/Ranchero-Software/NetNewsWire/graphs/contributors"}}{\fldrslt many more}}!\
\
\pard\pardeftab720\sa60\partightenfactor0
@ -44,5 +47,5 @@ Difficult infrastructure by {\field{\*\fldinst{HYPERLINK "https://rhonabwy.com/"
\f0\b \cf2 Dedication:\
\pard\pardeftab720\li360\sa60\partightenfactor0
\f1\b0 \cf2 NetNewsWire 5.0 is dedicated to Aaron Swartz, Derek Miller, and Alex King, who helped with earlier versions of NetNewsWire, who I miss.\
\f1\b0 \cf2 NetNewsWire 5.1 is dedicated to everyone working to save democracy around the world.\
}

View File

@ -52,7 +52,7 @@
<key>NSAppleScriptEnabled</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2002-2019 Brent Simmons. All rights reserved.</string>
<string>Copyright © 2002-2020 Brent Simmons. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Main</string>
<key>NSPrincipalClass</key>
@ -60,9 +60,9 @@
<key>OSAScriptingDefinition</key>
<string>NetNewsWire.sdef</string>
<key>SUFeedURL</key>
<string>https://ranchero.com/downloads/netnewswire-5.1-beta.xml</string>
<string>https://ranchero.com/downloads/netnewswire-release.xml</string>
<key>FeedURLForTestBuilds</key>
<string>https://ranchero.com/downloads/netnewswire-5.1-beta.xml</string>
<string>https://ranchero.com/downloads/netnewswire-beta.xml</string>
<key>UserAgent</key>
<string>NetNewsWire (RSS Reader; https://ranchero.com/netnewswire/)</string>
<key>OrganizationIdentifier</key>

View File

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<false/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>

View File

@ -12,18 +12,8 @@
<array>
<string>CloudKit</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.$(ORGANIZATION_IDENTIFIER).NetNewsWire</string>
</array>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.temporary-exception.apple-events</key>
<array>
<string>com.red-sweater.marsedit4</string>

View File

@ -57,7 +57,7 @@
</dict>
</dict>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2019 Ranchero Software. All rights reserved.</string>
<string>Copyright © 2019-2020 Brent Simmons. All rights reserved.</string>
<key>NSHumanReadableDescription</key>
<string>This extension adds a Safari toolbar button for easily subscribing to the syndication feed for the current page.</string>
</dict>

View File

@ -50,8 +50,8 @@
</dict>
</array>
<key>SUFeedURL</key>
<string>https://ranchero.com/downloads/netnewswire-5.1-beta.xml</string>
<string>https://ranchero.com/downloads/netnewswire-release.xml</string>
<key>FeedURLForTestBuilds</key>
<string>https://ranchero.com/downloads/netnewswire-5.1-beta.xml</string>
<string>https://ranchero.com/downloads/netnewswire-beta.xml</string>
</dict>
</plist>

View File

@ -277,8 +277,14 @@ extension AddAccountModel {
// MARK:- OAuthAccountAuthorizationOperationDelegate
extension AddAccountModel: OAuthAccountAuthorizationOperationDelegate {
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) {
accountIsAuthenticating = false
accountAdded = true
// macOS only: `ASWebAuthenticationSession` leaves the browser in the foreground.
// Ensure the app is in the foreground so the user can see their Feedly account load.
NSApplication.shared.activate(ignoringOtherApps: true)
account.refreshAll { [weak self] result in
switch result {
case .success:
@ -291,6 +297,11 @@ extension AddAccountModel: OAuthAccountAuthorizationOperationDelegate {
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) {
accountIsAuthenticating = false
// macOS only: `ASWebAuthenticationSession` leaves the browser in the foreground.
// Ensure the app is in the foreground so the user can see the error.
NSApplication.shared.activate(ignoringOtherApps: true)
addAccountError = .other(error: error)
}
}

View File

@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<false/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>

View File

@ -13,7 +13,7 @@
<string>CloudKit</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true/>
<false/>
<key>com.apple.security.automation.apple-events</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>

View File

@ -32,18 +32,18 @@ a:hover {
--header-color: rgba(0, 0, 0, 0.3);
--body-code-color: #666;
--system-message-color: #cbcbcb;
--feedlink-color: rgba(0, 0, 0, 0.3);
--feedlink-color: rgba(255, 0, 0, 0.6);
--article-title-color: #333;
--article-date-color: rgba(0, 0, 0, 0.5);
--article-date-color: rgba(0, 0, 0, 0.3);
--table-cell-border-color: lightgray;
}
@media(prefers-color-scheme: dark) {
:root {
--header-color: #d2d2d2;
--header-color: rgba(94, 158, 244, 1);
--body-code-color: #b2b2b2;
--system-message-color: #5f5f5f;
--feedlink-color: rgba(255, 255, 255, 0.7);
--feedlink-color: rgba(94, 158, 244, 1);
--article-title-color: #e0e0e0;
--article-date-color: rgba(255, 255, 255, 0.5);
--table-cell-border-color: dimgray;
@ -57,10 +57,14 @@ body {
body .headerTable {
border-bottom: 1px solid var(--header-table-border-color);
color: var(--header-color);
}
body .header {
color: var(--header-color);
}
body .header a:link, .header a:visited {
color: var(--header-color);
}
body code, body pre {
color: var(--body-code-color);

View File

@ -19,17 +19,17 @@ typealias FetchRequestOperationResultBlock = (Set<Article>, FetchRequestOperatio
final class FetchRequestOperation {
let id: Int
let readFilter: Bool
let readFilterEnabledTable: [FeedIdentifier: Bool]
let resultBlock: FetchRequestOperationResultBlock
var isCanceled = false
var isFinished = false
private let representedObjects: [Any]
private let fetchers: [ArticleFetcher]
init(id: Int, readFilter: Bool, representedObjects: [Any], resultBlock: @escaping FetchRequestOperationResultBlock) {
init(id: Int, readFilterEnabledTable: [FeedIdentifier: Bool], fetchers: [ArticleFetcher], resultBlock: @escaping FetchRequestOperationResultBlock) {
precondition(Thread.isMainThread)
self.id = id
self.readFilter = readFilter
self.representedObjects = representedObjects
self.readFilterEnabledTable = readFilterEnabledTable
self.fetchers = fetchers
self.resultBlock = resultBlock
}
@ -51,15 +51,14 @@ final class FetchRequestOperation {
return
}
let articleFetchers = representedObjects.compactMap{ $0 as? ArticleFetcher }
if articleFetchers.isEmpty {
if fetchers.isEmpty {
isFinished = true
resultBlock(Set<Article>(), self)
callCompletionIfNeeded()
return
}
let numberOfFetchers = articleFetchers.count
let numberOfFetchers = fetchers.count
var fetchersReturned = 0
var fetchedArticles = Set<Article>()
@ -81,19 +80,19 @@ final class FetchRequestOperation {
}
}
for articleFetcher in articleFetchers {
if readFilter {
articleFetcher.fetchUnreadArticlesAsync { articleSetResult in
let articles = (try? articleSetResult.get()) ?? Set<Article>()
process(articles)
}
}
else {
articleFetcher.fetchArticlesAsync { articleSetResult in
for fetcher in fetchers {
if (fetcher as? Feed)?.readFiltered(readFilterEnabledTable: readFilterEnabledTable) ?? true {
fetcher.fetchUnreadArticlesAsync { articleSetResult in
let articles = (try? articleSetResult.get()) ?? Set<Article>()
process(articles)
}
} else {
fetcher.fetchArticlesAsync { articleSetResult in
let articles = (try? articleSetResult.get()) ?? Set<Article>()
process(articles)
}
}
}
}
}

View File

@ -100,6 +100,7 @@ class AddFeedViewController: UITableViewController {
let normalizedURLString = urlString.normalizedURL
guard !normalizedURLString.isEmpty, let url = URL(unicodeString: normalizedURLString) else {
delegate?.processingDidCancel()
return
}

View File

@ -1928,7 +1928,8 @@ private extension SceneCoordinator {
precondition(Thread.isMainThread)
cancelPendingAsyncFetches()
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, readFilter: isReadArticlesFiltered, representedObjects: representedObjects) { [weak self] (articles, operation) in
let fetchers = representedObjects.compactMap { $0 as? ArticleFetcher }
let fetchOperation = FetchRequestOperation(id: fetchSerialNumber, readFilterEnabledTable: readFilterEnabledTable, fetchers: fetchers) { [weak self] (articles, operation) in
precondition(Thread.isMainThread)
guard !operation.isCanceled, let strongSelf = self, operation.id == strongSelf.fetchSerialNumber else {
return

View File

@ -1,6 +1,6 @@
// High Level Settings common to both the Mac application and any extensions we bundle with it
MARKETING_VERSION = 5.1d3
CURRENT_PROJECT_VERSION = 3003
MARKETING_VERSION = 5.1.1
CURRENT_PROJECT_VERSION = 3012
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon