This commit is contained in:
Brent Simmons
2020-11-06 17:21:24 -08:00
62 changed files with 1310 additions and 809 deletions

View File

@@ -20,7 +20,7 @@ class FeedFinder {
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), urlComponents.host == "micro.blog" {
urlComponents.path = "\(urlComponents.path).json"
if let newURLString = urlComponents.url?.absoluteString {
let microblogFeedSpecifier = FeedSpecifier(title: nil, urlString: newURLString, source: .HTMLLink)
let microblogFeedSpecifier = FeedSpecifier(title: nil, urlString: newURLString, source: .HTMLLink, orderFound: 1)
completion(.success(Set([microblogFeedSpecifier])))
}
} else {
@@ -45,7 +45,7 @@ class FeedFinder {
}
if FeedFinder.isFeed(data, url.absoluteString) {
let feedSpecifier = FeedSpecifier(title: nil, urlString: url.absoluteString, source: .UserEntered)
let feedSpecifier = FeedSpecifier(title: nil, urlString: url.absoluteString, source: .UserEntered, orderFound: 1)
completion(.success(Set([feedSpecifier])))
return
}
@@ -120,11 +120,11 @@ private extension FeedFinder {
// Its also fairly common for /index.xml to work.
if let url = URL(string: urlString) {
let feedURL = url.appendingPathComponent("feed", isDirectory: true)
let wordpressFeedSpecifier = FeedSpecifier(title: nil, urlString: feedURL.absoluteString, source: .HTMLLink)
let wordpressFeedSpecifier = FeedSpecifier(title: nil, urlString: feedURL.absoluteString, source: .HTMLLink, orderFound: 1)
feedSpecifiers.insert(wordpressFeedSpecifier)
let indexXMLURL = url.appendingPathComponent("index.xml", isDirectory: false)
let indexXMLFeedSpecifier = FeedSpecifier(title: nil, urlString: indexXMLURL.absoluteString, source: .HTMLLink)
let indexXMLFeedSpecifier = FeedSpecifier(title: nil, urlString: indexXMLURL.absoluteString, source: .HTMLLink, orderFound: 1)
feedSpecifiers.insert(indexXMLFeedSpecifier)
}
}

View File

@@ -21,6 +21,7 @@ struct FeedSpecifier: Hashable {
public let title: String?
public let urlString: String
public let source: Source
public let orderFound: Int
public var score: Int {
return calculatedScore()
}
@@ -30,8 +31,9 @@ struct FeedSpecifier: Hashable {
let mergedTitle = title ?? feedSpecifier.title
let mergedSource = source.equalToOrBetterThan(feedSpecifier.source) ? source : feedSpecifier.source
let mergedOrderFound = orderFound < feedSpecifier.orderFound ? orderFound : feedSpecifier.orderFound
return FeedSpecifier(title: mergedTitle, urlString: urlString, source: mergedSource)
return FeedSpecifier(title: mergedTitle, urlString: urlString, source: mergedSource, orderFound: mergedOrderFound)
}
public static func bestFeed(in feedSpecifiers: Set<FeedSpecifier>) -> FeedSpecifier? {
@@ -69,6 +71,8 @@ private extension FeedSpecifier {
score = score + 50
}
score = score - ((orderFound - 1) * 5)
if urlString.caseInsensitiveContains("comments") {
score = score - 10
}

View File

@@ -21,22 +21,24 @@ class HTMLFeedFinder {
init(parserData: ParserData) {
let metadata = RSHTMLMetadataParser.htmlMetadata(with: parserData)
var orderFound = 0
for oneFeedLink in metadata.feedLinks {
if let oneURLString = oneFeedLink.urlString?.normalizedURL {
let oneFeedSpecifier = FeedSpecifier(title: oneFeedLink.title, urlString: oneURLString, source: .HTMLHead)
orderFound = orderFound + 1
let oneFeedSpecifier = FeedSpecifier(title: oneFeedLink.title, urlString: oneURLString, source: .HTMLHead, orderFound: orderFound)
addFeedSpecifier(oneFeedSpecifier)
}
}
let bodyLinks = RSHTMLLinkParser.htmlLinks(with: parserData)
for oneBodyLink in bodyLinks {
if linkMightBeFeed(oneBodyLink), let normalizedURL = oneBodyLink.urlString?.normalizedURL {
let oneFeedSpecifier = FeedSpecifier(title: oneBodyLink.text, urlString: normalizedURL, source: .HTMLLink)
addFeedSpecifier(oneFeedSpecifier)
}
for oneBodyLink in bodyLinks {
if linkMightBeFeed(oneBodyLink), let normalizedURL = oneBodyLink.urlString?.normalizedURL {
orderFound = orderFound + 1
let oneFeedSpecifier = FeedSpecifier(title: oneBodyLink.text, urlString: normalizedURL, source: .HTMLLink, orderFound: orderFound)
addFeedSpecifier(oneFeedSpecifier)
}
}
}
}

View File

@@ -34,6 +34,7 @@ final class FeedbinAPICaller: NSObject {
private let feedbinBaseURL = URL(string: "https://api.feedbin.com/v2/")!
private var transport: Transport!
private var suspended = false
private var lastBackdateStartTime: Date?
var credentials: Credentials?
weak var accountMetadata: AccountMetadata?
@@ -486,10 +487,26 @@ final class FeedbinAPICaller: NSObject {
}
func retrieveEntries(completion: @escaping (Result<([FeedbinEntry]?, String?, Date?, Int?), Error>) -> Void) {
// If this is an initial sync, go and grab the previous 3 months of entries. If not, use the last
// article fetch to only get the articles **published** since the last article fetch.
//
// We do a backdate fetch every launch or every 24 hours. This will help with
// getting **updated** articles that normally wouldn't be found with a regular fetch.
// https://github.com/Ranchero-Software/NetNewsWire/issues/2549#issuecomment-722341356
let since: Date = {
if let lastArticleFetch = accountMetadata?.lastArticleFetchStartTime {
return lastArticleFetch
if let lastBackdateStartTime = lastBackdateStartTime {
if lastBackdateStartTime.byAdding(days: 1) < lastArticleFetch {
self.lastBackdateStartTime = lastArticleFetch
return lastArticleFetch.bySubtracting(days: 1)
} else {
return lastArticleFetch
}
} else {
self.lastBackdateStartTime = lastArticleFetch
return lastArticleFetch.bySubtracting(days: 1)
}
} else {
return Calendar.current.date(byAdding: .month, value: -3, to: Date()) ?? Date()
}

View File

@@ -986,27 +986,22 @@ private extension FeedbinAccountDelegate {
}
func decideBestFeedChoice(account: Account, url: String, name: String?, container: Container, choices: [FeedbinSubscriptionChoice], completion: @escaping (Result<WebFeed, Error>) -> Void) {
var orderFound = 0
let feedSpecifiers: [FeedSpecifier] = choices.map { choice in
let source = url == choice.url ? FeedSpecifier.Source.UserEntered : FeedSpecifier.Source.HTMLLink
let specifier = FeedSpecifier(title: choice.name, urlString: choice.url, source: source)
orderFound = orderFound + 1
let specifier = FeedSpecifier(title: choice.name, urlString: choice.url, source: source, orderFound: orderFound)
return specifier
}
if let bestSpecifier = FeedSpecifier.bestFeed(in: Set(feedSpecifiers)) {
if let bestSubscription = choices.filter({ bestSpecifier.urlString == $0.url }).first {
createWebFeed(for: account, url: bestSubscription.url, name: name, container: container, completion: completion)
} else {
DispatchQueue.main.async {
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
}
}
createWebFeed(for: account, url: bestSpecifier.urlString, name: name, container: container, completion: completion)
} else {
DispatchQueue.main.async {
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
}
}
}
func createFeed( account: Account, subscription sub: FeedbinSubscription, name: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {

View File

@@ -235,6 +235,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
switch result {
case .success:
DispatchQueue.main.async {
folder.externalID = "user/-/label/\(name)"
folder.name = name
completion(.success(()))
}
@@ -298,13 +299,18 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
}
group.notify(queue: DispatchQueue.main) {
self.caller.deleteTag(folder: folder) { result in
switch result {
case .success:
account.removeFolder(folder)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
if self.variant == .theOldReader {
account.removeFolder(folder)
completion(.success(()))
} else {
self.caller.deleteTag(folder: folder) { result in
switch result {
case .success:
account.removeFolder(folder)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
}
}
}
}
@@ -821,30 +827,6 @@ private extension ReaderAPIAccountDelegate {
feed.folderRelationship = [folderExternalID: feedExternalID]
}
}
func decideBestFeedChoice(account: Account, url: String, name: String?, container: Container, choices: [ReaderAPISubscriptionChoice], completion: @escaping (Result<WebFeed, Error>) -> Void) {
let feedSpecifiers: [FeedSpecifier] = choices.map { choice in
let source = url == choice.url ? FeedSpecifier.Source.UserEntered : FeedSpecifier.Source.HTMLLink
let specifier = FeedSpecifier(title: choice.name, urlString: choice.url, source: source)
return specifier
}
if let bestSpecifier = FeedSpecifier.bestFeed(in: Set(feedSpecifiers)) {
if let bestSubscription = choices.filter({ bestSpecifier.urlString == $0.url }).first {
createWebFeed(for: account, url: bestSubscription.url, name: name, container: container, completion: completion)
} else {
DispatchQueue.main.async {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
}
}
} else {
DispatchQueue.main.async {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
}
}
}
func createFeed( account: Account, subscription sub: ReaderAPISubscription, name: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
@@ -857,7 +839,7 @@ private extension ReaderAPIAccountDelegate {
switch result {
case .success:
if let name = name {
account.renameWebFeed(feed, to: name) { result in
self.renameWebFeed(for: account, with: feed, to: name) { result in
switch result {
case .success:
self.initialFeedDownload(account: account, feed: feed, completion: completion)

View File

@@ -49,20 +49,22 @@ final class ReaderAPICaller: NSObject {
}
private var transport: Transport!
var variant: ReaderAPIVariant = .generic
var credentials: Credentials?
private let uriComponentAllowed: CharacterSet
private var accessToken: String?
weak var accountMetadata: AccountMetadata?
var variant: ReaderAPIVariant = .generic
var credentials: Credentials?
var server: String? {
get {
return APIBaseURL?.host
return apiBaseURL?.host
}
}
private var APIBaseURL: URL? {
private var apiBaseURL: URL? {
get {
switch variant {
case .generic, .freshRSS:
@@ -77,8 +79,13 @@ final class ReaderAPICaller: NSObject {
}
init(transport: Transport) {
super.init()
self.transport = transport
var urlHostAllowed = CharacterSet.urlHostAllowed
urlHostAllowed.remove("+")
urlHostAllowed.remove("&")
uriComponentAllowed = urlHostAllowed
super.init()
}
func cancelAll() {
@@ -170,7 +177,7 @@ final class ReaderAPICaller: NSObject {
func retrieveTags(completion: @escaping (Result<[ReaderAPITag]?, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -199,7 +206,7 @@ final class ReaderAPICaller: NSObject {
}
func renameTag(oldName: String, newName: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -212,8 +219,13 @@ final class ReaderAPICaller: NSObject {
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let oldTagName = "user/-/label/\(oldName)"
let newTagName = "user/-/label/\(newName)"
guard let encodedOldName = self.encodeForURLPath(oldName), let encodedNewName = self.encodeForURLPath(newName) else {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
return
}
let oldTagName = "user/-/label/\(encodedOldName)"
let newTagName = "user/-/label/\(encodedNewName)"
let postData = "T=\(token)&s=\(oldTagName)&dest=\(newTagName)".data(using: String.Encoding.utf8)
self.transport.send(request: request, method: HTTPMethod.post, payload: postData!, completion: { (result) in
@@ -235,7 +247,7 @@ final class ReaderAPICaller: NSObject {
}
func deleteTag(folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -274,7 +286,7 @@ final class ReaderAPICaller: NSObject {
}
func retrieveSubscriptions(completion: @escaping (Result<[ReaderAPISubscription]?, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -302,7 +314,7 @@ final class ReaderAPICaller: NSObject {
}
func createSubscription(url: String, name: String?, folder: Folder?, completion: @escaping (Result<CreateReaderAPISubscriptionResult, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -376,10 +388,10 @@ final class ReaderAPICaller: NSObject {
request.httpMethod = "POST"
var postString = "T=\(token)&ac=subscribe&s=\(streamId)"
if let folder = folder {
postString += "&a=user/-/label/\(folder.nameForDisplay)"
if let folderName = self.encodeForURLPath(folder?.nameForDisplay) {
postString += "&a=user/-/label/\(folderName)"
}
if let name = name {
if let name = self.encodeForURLPath(name) {
postString += "&t=\(name)"
}
@@ -411,7 +423,7 @@ final class ReaderAPICaller: NSObject {
}
func renameSubscription(subscriptionID: String, newName: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -424,7 +436,12 @@ final class ReaderAPICaller: NSObject {
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let postData = "T=\(token)&s=\(subscriptionID)&ac=edit&t=\(newName)".data(using: String.Encoding.utf8)
guard let encodedNewName = self.encodeForURLPath(newName) else {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
return
}
let postData = "T=\(token)&s=\(subscriptionID)&ac=edit&t=\(encodedNewName)".data(using: String.Encoding.utf8)
self.transport.send(request: request, method: HTTPMethod.post, payload: postData!, completion: { (result) in
switch result {
@@ -445,7 +462,7 @@ final class ReaderAPICaller: NSObject {
}
func deleteSubscription(subscriptionID: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -479,7 +496,7 @@ final class ReaderAPICaller: NSObject {
func createTagging(subscriptionID: String, tagName: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -492,8 +509,12 @@ final class ReaderAPICaller: NSObject {
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
let tagName = "user/-/label/\(tagName)"
let postData = "T=\(token)&s=\(subscriptionID)&ac=edit&a=\(tagName)".data(using: String.Encoding.utf8)
guard let tagName = self.encodeForURLPath(tagName) else {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
return
}
let postData = "T=\(token)&s=\(subscriptionID)&ac=edit&a=user/-/label/\(tagName)".data(using: String.Encoding.utf8)
self.transport.send(request: request, method: HTTPMethod.post, payload: postData!, completion: { (result) in
switch result {
@@ -514,7 +535,7 @@ final class ReaderAPICaller: NSObject {
}
func deleteTagging(subscriptionID: String, tagName: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -554,7 +575,7 @@ final class ReaderAPICaller: NSObject {
return
}
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -603,7 +624,7 @@ final class ReaderAPICaller: NSObject {
}
func retrieveItemIDs(type: ItemIDType, webFeedID: String? = nil, completion: @escaping ((Result<[String], Error>) -> Void)) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}
@@ -734,6 +755,11 @@ final class ReaderAPICaller: NSObject {
private extension ReaderAPICaller {
func encodeForURLPath(_ pathComponent: String?) -> String? {
guard let pathComponent = pathComponent else { return nil }
return pathComponent.addingPercentEncoding(withAllowedCharacters: uriComponentAllowed)
}
func storeConditionalGet(key: String, headers: [AnyHashable : Any]) {
if var conditionalGet = accountMetadata?.conditionalGetInfo {
conditionalGet[key] = HTTPConditionalGetInfo(headers: headers)
@@ -749,7 +775,7 @@ private extension ReaderAPICaller {
}
private func updateStateToEntries(entries: [String], state: ReaderState, add: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
guard let baseURL = APIBaseURL else {
guard let baseURL = apiBaseURL else {
completion(.failure(CredentialsError.incompleteCredentials))
return
}

View File

@@ -30,6 +30,7 @@ final class AppDefaults {
static let timelineGroupByFeed = "timelineGroupByFeed"
static let detailFontSize = "detailFontSize"
static let openInBrowserInBackground = "openInBrowserInBackground"
static let articleTextSize = "articleTextSize"
static let refreshInterval = "refreshInterval"
static let addWebFeedAccountID = "addWebFeedAccountID"
static let addWebFeedFolderName = "addWebFeedFolderName"
@@ -244,6 +245,16 @@ final class AppDefaults {
return AppDefaults.bool(for: Key.timelineShowsSeparators)
}
var articleTextSize: ArticleTextSize {
get {
let rawValue = UserDefaults.standard.integer(forKey: Key.articleTextSize)
return ArticleTextSize(rawValue: rawValue) ?? ArticleTextSize.large
}
set {
UserDefaults.standard.set(newValue.rawValue, forKey: Key.articleTextSize)
}
}
var refreshInterval: RefreshInterval {
get {
let rawValue = UserDefaults.standard.integer(forKey: Key.refreshInterval)

View File

@@ -25,21 +25,115 @@
</windowController>
<customObject id="Q6y-w1-UqW" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-573" y="41"/>
<point key="canvasLocation" x="-573" y="40.5"/>
</scene>
<!--General-->
<scene sceneID="R4l-Wg-k7x">
<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="506" height="210"/>
<rect key="frame" x="0.0" y="0.0" width="509" height="247"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Ut3-yd-q6G">
<rect key="frame" x="36" y="16" width="433" height="178"/>
<rect key="frame" x="54" y="16" width="400" height="215"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pR2-Bf-7Fd">
<rect key="frame" x="6" y="194" width="106" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Article Text Size:" id="xQu-QV-91i">
<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="Z6O-Zt-V1g">
<rect key="frame" x="115" y="187" width="289" height="25"/>
<popUpButtonCell key="cell" type="push" title="Medium" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" tag="2" imageScaling="proportionallyDown" inset="2" selectedItem="jMV-2o-5Oh" id="6pw-Vq-tjM">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="Yqo-qR-1qN">
<items>
<menuItem title="Small" tag="1" id="roB-Mu-Ht7"/>
<menuItem title="Medium" state="on" tag="2" id="jMV-2o-5Oh"/>
<menuItem title="Large" tag="3" id="ckZ-0Q-rNz"/>
<menuItem title="X-Large" tag="4" id="qMe-6g-Vme"/>
<menuItem title="XX-Large" tag="5" id="4Pi-2Y-XmV"/>
</items>
</menu>
<connections>
<binding destination="mAF-gO-1PI" name="selectedTag" keyPath="values.articleTextSize" id="qgn-ap-aCc">
<dictionary key="options">
<bool key="NSAllowsEditingMultipleValuesSelection" value="NO"/>
<bool key="NSConditionallySetsEnabled" value="NO"/>
<integer key="NSMultipleValuesPlaceholder" value="3"/>
<integer key="NSNoSelectionPlaceholder" value="3"/>
<integer key="NSNotApplicablePlaceholder" value="3"/>
<integer key="NSNullPlaceholder" value="3"/>
<bool key="NSRaisesForNotApplicableKeys" value="NO"/>
</dictionary>
</binding>
</connections>
</popUpButtonCell>
</popUpButton>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="Tdg-6Y-gvW">
<rect key="frame" x="0.0" y="172" width="400" height="5"/>
</box>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Wsb-Lr-8Q7">
<rect key="frame" x="54" y="141" 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="Ci4-fW-KjU">
<rect key="frame" x="115" y="134" 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="j1i-Ev-7rI">
<items>
<menuItem title="Safari" state="on" id="ObP-qK-qDJ"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="browserPopUpDidChangeValue:" target="iuH-lz-18x" id="vSr-98-LQL"/>
</connections>
</popUpButton>
<button horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Ubm-Pk-l7x">
<rect key="frame" x="116" y="107" 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>
<connections>
<binding destination="mAF-gO-1PI" name="value" keyPath="values.openInBrowserInBackground" id="hKH-w2-y6N">
<dictionary key="options">
<bool key="NSAllowsEditingMultipleValuesSelection" value="NO"/>
<bool key="NSConditionallySetsEnabled" value="NO"/>
<integer key="NSMultipleValuesPlaceholder" value="0"/>
<integer key="NSNoSelectionPlaceholder" value="0"/>
<integer key="NSNotApplicablePlaceholder" value="0"/>
<integer key="NSNullPlaceholder" value="0"/>
<bool key="NSRaisesForNotApplicableKeys" value="NO"/>
</dictionary>
</binding>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="j0t-Wa-UTL">
<rect key="frame" x="135" y="84" 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>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="hQy-ng-ijd">
<rect key="frame" x="0.0" y="65" width="400" height="5"/>
</box>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ucw-vG-yLt">
<rect key="frame" x="53" y="157" width="92" height="16"/>
<rect key="frame" x="20" y="34" 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 +141,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SFF-mL-yc8">
<rect key="frame" x="148" y="150" width="289" height="25"/>
<rect key="frame" x="115" y="27" width="289" height="25"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="N1a-qV-4Os"/>
</constraints>
@@ -81,51 +175,8 @@
</connections>
</popUpButtonCell>
</popUpButton>
<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="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="j1i-Ev-7rI">
<items>
<menuItem title="Safari" state="on" id="ObP-qK-qDJ"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="browserPopUpDidChangeValue:" target="iuH-lz-18x" id="vSr-98-LQL"/>
</connections>
</popUpButton>
<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>
<connections>
<binding destination="mAF-gO-1PI" name="value" keyPath="values.openInBrowserInBackground" id="hKH-w2-y6N">
<dictionary key="options">
<bool key="NSAllowsEditingMultipleValuesSelection" value="NO"/>
<bool key="NSConditionallySetsEnabled" value="NO"/>
<integer key="NSMultipleValuesPlaceholder" value="0"/>
<integer key="NSNoSelectionPlaceholder" value="0"/>
<integer key="NSNotApplicablePlaceholder" value="0"/>
<integer key="NSNullPlaceholder" value="0"/>
<bool key="NSRaisesForNotApplicableKeys" value="NO"/>
</dictionary>
</binding>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jVd-Ie-CGX">
<rect key="frame" x="77" y="4" width="68" height="16"/>
<rect key="frame" x="44" 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,7 +184,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mwT-nY-TrX">
<rect key="frame" x="149" y="3" width="143" height="18"/>
<rect key="frame" x="116" 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"/>
@@ -142,72 +193,44 @@
<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="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>
<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="maY-sW-bmj">
<items>
<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="rssReaderPopupDidChangeValue:" target="iuH-lz-18x" id="wH0-4N-Exe"/>
</connections>
</popUpButton>
</subviews>
<constraints>
<constraint firstItem="Wsb-Lr-8Q7" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="17A-5m-ZG0"/>
<constraint firstItem="Tdg-6Y-gvW" firstAttribute="top" secondItem="Z6O-Zt-V1g" secondAttribute="bottom" constant="16" id="2iQ-Xt-RLM"/>
<constraint firstItem="Z6O-Zt-V1g" firstAttribute="leading" secondItem="pR2-Bf-7Fd" secondAttribute="trailing" constant="8" symbolic="YES" id="2wM-K6-eAF"/>
<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" 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="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="mwT-nY-TrX" firstAttribute="top" secondItem="ucw-vG-yLt" secondAttribute="bottom" constant="14" id="CZq-CZ-fC5"/>
<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="Z6O-Zt-V1g" firstAttribute="top" secondItem="Ut3-yd-q6G" secondAttribute="top" constant="4" id="IaE-jL-vMM"/>
<constraint firstItem="hQy-ng-ijd" firstAttribute="leading" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="KEI-R5-rzD"/>
<constraint firstItem="pR2-Bf-7Fd" firstAttribute="leading" secondItem="Ut3-yd-q6G" secondAttribute="leading" constant="8" id="LRG-HZ-yxh"/>
<constraint firstAttribute="trailing" secondItem="SFF-mL-yc8" secondAttribute="trailing" id="N39-Q9-X5Q"/>
<constraint firstAttribute="trailing" secondItem="hQy-ng-ijd" secondAttribute="trailing" id="RbT-jK-fBb"/>
<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="hQy-ng-ijd" firstAttribute="top" secondItem="j0t-Wa-UTL" secondAttribute="bottom" constant="16" id="Zk2-TC-NRS"/>
<constraint firstItem="pR2-Bf-7Fd" firstAttribute="firstBaseline" secondItem="Z6O-Zt-V1g" secondAttribute="firstBaseline" id="aO5-iE-L7A"/>
<constraint firstAttribute="trailing" secondItem="Z6O-Zt-V1g" secondAttribute="trailing" id="aS9-KA-vSH"/>
<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="Tdg-6Y-gvW" firstAttribute="leading" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="b3I-JF-If3"/>
<constraint firstItem="jVd-Ie-CGX" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Ut3-yd-q6G" secondAttribute="leading" id="dDb-jw-vca"/>
<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="SFF-mL-yc8" firstAttribute="top" secondItem="hQy-ng-ijd" secondAttribute="bottom" constant="16" id="eM7-OM-Qsz"/>
<constraint firstItem="mwT-nY-TrX" firstAttribute="leading" secondItem="Ubm-Pk-l7x" secondAttribute="leading" id="fb7-Og-rSp"/>
<constraint firstItem="ulQ-kB-eNO" firstAttribute="centerY" secondItem="48p-6Z-Yid" secondAttribute="centerY" id="gSz-1T-Zp7"/>
<constraint firstItem="Ci4-fW-KjU" firstAttribute="top" secondItem="Tdg-6Y-gvW" secondAttribute="bottom" constant="16" id="hXl-1D-lTD"/>
<constraint firstAttribute="bottom" secondItem="mwT-nY-TrX" secondAttribute="bottom" constant="4" id="jFE-ye-pSr"/>
<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="Z6O-Zt-V1g" firstAttribute="width" secondItem="SFF-mL-yc8" secondAttribute="width" id="noW-Jf-Xbs"/>
<constraint firstAttribute="trailing" secondItem="Tdg-6Y-gvW" secondAttribute="trailing" id="qzz-gu-8kO"/>
<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="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>
@@ -219,29 +242,29 @@
</constraints>
</view>
<connections>
<outlet property="articleTextSizePopup" destination="Z6O-Zt-V1g" id="qNu-i3-fFB"/>
<outlet property="defaultBrowserPopup" destination="Ci4-fW-KjU" id="7Nh-nU-Sbc"/>
<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="-570" y="395"/>
<point key="canvasLocation" x="-568.5" y="438.5"/>
</scene>
<!--Advanced Preferences View Controller-->
<scene sceneID="z1G-rc-sP5">
<objects>
<viewController storyboardIdentifier="Advanced" id="GNh-Wp-giO" customClass="AdvancedPreferencesViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="Hij-7D-6Pw">
<rect key="frame" x="0.0" y="0.0" width="450" height="278"/>
<rect key="frame" x="0.0" y="0.0" width="450" height="290"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="uJD-OF-YVY">
<rect key="frame" x="60" y="20" width="330" height="238"/>
<rect key="frame" x="60" y="20" width="330" height="250"/>
<subviews>
<textField horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="EH5-aS-E55">
<rect key="frame" x="-2" y="222" width="87" height="16"/>
<rect key="frame" x="-2" y="234" width="87" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="App Updates:" id="zqG-X2-E9b">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -249,7 +272,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="T4A-0o-p2w">
<rect key="frame" x="89" y="221" width="149" height="18"/>
<rect key="frame" x="89" y="233" width="149" height="18"/>
<buttonCell key="cell" type="check" title="Check automatically" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="dm8-Xy-0Ba">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -259,7 +282,7 @@
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Q6M-Iz-Ypx">
<rect key="frame" x="17" y="194" width="68" height="16"/>
<rect key="frame" x="17" y="206" width="68" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Download:" id="6bb-c0-guo">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -267,7 +290,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="QCu-J4-0yV">
<rect key="frame" x="89" y="193" width="114" height="18"/>
<rect key="frame" x="89" y="205" width="114" height="18"/>
<buttonCell key="cell" type="radio" title="Release builds" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="F8M-rS-und">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -277,7 +300,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CeE-AE-hRG">
<rect key="frame" x="89" y="171" width="92" height="18"/>
<rect key="frame" x="89" y="183" width="92" height="18"/>
<buttonCell key="cell" type="radio" title="Test builds" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="Fuf-rU-D6M">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -287,7 +310,7 @@
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="MzL-QQ-2oL">
<rect key="frame" x="-2" y="116" width="334" height="48"/>
<rect key="frame" x="-2" y="128" width="334" height="48"/>
<constraints>
<constraint firstAttribute="width" constant="330" id="jf8-5e-Eij"/>
</constraints>
@@ -298,7 +321,7 @@
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="TKI-a9-bRX">
<rect key="frame" x="84" y="81" width="148" height="32"/>
<rect key="frame" x="84" y="93" 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"/>
@@ -308,7 +331,7 @@
</connections>
</button>
<textField horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="SUN-k3-ZEb">
<rect key="frame" x="12" y="40" width="73" height="16"/>
<rect key="frame" x="12" y="52" width="73" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Crash logs:" id="qcq-fU-Ks0">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -316,7 +339,7 @@
</textFieldCell>
</textField>
<button horizontalHuggingPriority="1000" verticalHuggingPriority="1000" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="UHg-1l-FlD">
<rect key="frame" x="89" y="39" width="142" height="18"/>
<rect key="frame" x="89" y="51" width="142" height="18"/>
<buttonCell key="cell" type="check" title="Send automatically" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="jnc-C5-4oI">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -345,6 +368,12 @@
<action selector="showPrivacyPolicy:" target="VX1-M3-K0J" id="s1x-cP-hGd"/>
</connections>
</button>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="UD5-5N-W4F">
<rect key="frame" x="0.0" y="77" width="330" height="5"/>
</box>
<box verticalHuggingPriority="750" boxType="separator" translatesAutoresizingMaskIntoConstraints="NO" id="B1Q-jV-3Yl">
<rect key="frame" x="0.0" y="33" width="330" height="5"/>
</box>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="MzL-QQ-2oL" secondAttribute="trailing" id="04t-Su-3fv"/>
@@ -354,6 +383,8 @@
<constraint firstItem="TKI-a9-bRX" firstAttribute="leading" secondItem="CeE-AE-hRG" secondAttribute="leading" id="6Sm-VV-Qda"/>
<constraint firstItem="EH5-aS-E55" firstAttribute="leading" secondItem="uJD-OF-YVY" secondAttribute="leading" id="6eS-X9-PTK"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="UHg-1l-FlD" secondAttribute="trailing" id="7gl-UP-wqg"/>
<constraint firstItem="UD5-5N-W4F" firstAttribute="leading" secondItem="uJD-OF-YVY" secondAttribute="leading" id="98g-T1-9fR"/>
<constraint firstItem="B1Q-jV-3Yl" firstAttribute="leading" secondItem="uJD-OF-YVY" secondAttribute="leading" id="Fxs-zN-mlV"/>
<constraint firstItem="UHg-1l-FlD" firstAttribute="firstBaseline" secondItem="SUN-k3-ZEb" secondAttribute="firstBaseline" id="MAL-Ip-mEN"/>
<constraint firstItem="MzL-QQ-2oL" firstAttribute="leading" secondItem="uJD-OF-YVY" secondAttribute="leading" id="MMt-v0-0gl"/>
<constraint firstItem="CeE-AE-hRG" firstAttribute="leading" secondItem="T4A-0o-p2w" secondAttribute="leading" id="NWB-BO-GtL"/>
@@ -363,15 +394,19 @@
<constraint firstItem="EH5-aS-E55" firstAttribute="top" secondItem="uJD-OF-YVY" secondAttribute="top" id="VDU-as-fdx"/>
<constraint firstAttribute="bottom" secondItem="uuc-f2-OFX" secondAttribute="bottom" id="YA7-Xm-cFO"/>
<constraint firstItem="Q6M-Iz-Ypx" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="uJD-OF-YVY" secondAttribute="leading" id="Ygv-ha-RLn"/>
<constraint firstItem="UD5-5N-W4F" firstAttribute="top" secondItem="TKI-a9-bRX" secondAttribute="bottom" constant="20" symbolic="YES" id="b3Y-RX-O4j"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="TKI-a9-bRX" secondAttribute="trailing" id="bLP-TU-TeL"/>
<constraint firstItem="SUN-k3-ZEb" firstAttribute="trailing" secondItem="Q6M-Iz-Ypx" secondAttribute="trailing" id="c23-mt-Mfd"/>
<constraint firstAttribute="trailing" secondItem="UD5-5N-W4F" secondAttribute="trailing" id="csy-6Y-uJq"/>
<constraint firstItem="SUN-k3-ZEb" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="uJD-OF-YVY" secondAttribute="leading" id="dj1-Uj-ibG"/>
<constraint firstItem="MzL-QQ-2oL" firstAttribute="top" secondItem="CeE-AE-hRG" secondAttribute="bottom" constant="8" symbolic="YES" id="emd-u5-hgZ"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="uuc-f2-OFX" secondAttribute="trailing" id="gBp-7A-zG6"/>
<constraint firstAttribute="trailing" secondItem="B1Q-jV-3Yl" secondAttribute="trailing" id="h6j-aP-oTs"/>
<constraint firstItem="CeE-AE-hRG" firstAttribute="top" secondItem="QCu-J4-0yV" secondAttribute="bottom" constant="6" symbolic="YES" id="hYd-1l-oMg"/>
<constraint firstItem="uuc-f2-OFX" firstAttribute="top" secondItem="UHg-1l-FlD" secondAttribute="bottom" constant="20" symbolic="YES" id="jcZ-pH-ABY"/>
<constraint firstItem="uuc-f2-OFX" firstAttribute="top" secondItem="UHg-1l-FlD" secondAttribute="bottom" constant="32" id="jcZ-pH-ABY"/>
<constraint firstItem="T4A-0o-p2w" firstAttribute="firstBaseline" secondItem="EH5-aS-E55" secondAttribute="firstBaseline" id="jvM-Qd-rbB"/>
<constraint firstItem="TKI-a9-bRX" firstAttribute="top" secondItem="MzL-QQ-2oL" secondAttribute="bottom" constant="8" symbolic="YES" id="lIt-NB-IL8"/>
<constraint firstItem="B1Q-jV-3Yl" firstAttribute="top" secondItem="UHg-1l-FlD" secondAttribute="bottom" constant="16" id="mlO-QB-xom"/>
<constraint firstItem="QCu-J4-0yV" firstAttribute="top" secondItem="T4A-0o-p2w" secondAttribute="bottom" constant="12" id="nBX-kW-ncB"/>
<constraint firstItem="T4A-0o-p2w" firstAttribute="leading" secondItem="EH5-aS-E55" secondAttribute="trailing" constant="8" symbolic="YES" id="oNo-Il-411"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="CeE-AE-hRG" secondAttribute="trailing" id="qB6-BG-c3D"/>
@@ -407,22 +442,22 @@
<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="180" height="294"/>
<rect key="frame" x="20" y="44" width="180" height="314"/>
<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="178" height="293"/>
<clipView key="contentView" ambiguous="YES" id="cil-Gq-akO">
<rect key="frame" x="0.0" y="0.0" width="178" height="293"/>
<rect key="frame" x="1" y="0.0" width="178" height="313"/>
<clipView key="contentView" id="cil-Gq-akO">
<rect key="frame" x="0.0" y="0.0" width="178" height="313"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" ambiguous="YES" 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="207" height="293"/>
<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="178" height="313"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="175" minWidth="40" maxWidth="1000" id="JSx-yi-vwt">
<tableColumn width="146" minWidth="40" maxWidth="1000" id="JSx-yi-vwt">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
@@ -435,7 +470,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="Cell" id="h2e-5a-qNO">
<rect key="frame" x="11" y="1" width="184" height="17"/>
<rect key="frame" x="11" y="1" width="155" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="27f-p8-Wnt">
@@ -447,7 +482,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="154" height="16"/>
<rect key="frame" x="26" y="1" width="125" 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"/>
@@ -523,7 +558,7 @@
<rect key="frame" x="83" y="20" width="117" height="24"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Y7D-xQ-wep">
<rect key="frame" x="208" y="20" width="222" height="318"/>
<rect key="frame" x="208" y="20" width="222" height="338"/>
</customView>
</subviews>
<constraints>
@@ -578,22 +613,22 @@
<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="180" height="287"/>
<rect key="frame" x="20" y="44" width="180" height="307"/>
<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="178" height="286"/>
<clipView key="contentView" ambiguous="YES" id="dXw-GY-TP8">
<rect key="frame" x="0.0" y="0.0" width="178" height="286"/>
<rect key="frame" x="1" y="0.0" width="178" height="306"/>
<clipView key="contentView" id="dXw-GY-TP8">
<rect key="frame" x="0.0" y="0.0" width="178" height="306"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" ambiguous="YES" 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="207" height="286"/>
<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="178" height="306"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn width="175" minWidth="40" maxWidth="1000" id="jBM-96-TEB">
<tableColumn width="146" minWidth="40" maxWidth="1000" id="jBM-96-TEB">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
@@ -606,7 +641,7 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView identifier="Cell" id="xQs-6E-Kpy">
<rect key="frame" x="11" y="1" width="184" height="17"/>
<rect key="frame" x="11" y="1" width="155" height="17"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kmG-vw-CbN">
@@ -618,7 +653,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="154" height="16"/>
<rect key="frame" x="26" y="1" width="125" 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"/>
@@ -690,7 +725,7 @@
<rect key="frame" x="83" y="20" width="117" height="24"/>
</customView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="N1N-pE-gBL">
<rect key="frame" x="208" y="20" width="222" height="311"/>
<rect key="frame" x="208" y="20" width="222" height="331"/>
</customView>
</subviews>
<constraints>

View File

@@ -39,6 +39,14 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
return nil
}
}
private var articleTextSize = AppDefaults.shared.articleTextSize {
didSet {
if articleTextSize != oldValue {
reloadHTML()
}
}
}
#if !MAC_APP_STORE
private var webInspectorEnabled: Bool {
@@ -64,7 +72,7 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
// Wrap the webview in a box configured with the same background color that the web view uses
let box = NSBox(frame: .zero)
box.boxType = .custom
box.borderType = .noBorder
box.isTransparent = true
box.titlePosition = .noTitle
box.contentViewMargins = .zero
box.fillColor = NSColor(named: "webviewBackgroundColor")!
@@ -118,7 +126,7 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
webView.loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL)
}
@@ -137,6 +145,10 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
reloadArticleImage()
}
@objc func userDefaultsDidChange(_ note: Notification) {
self.articleTextSize = AppDefaults.shared.articleTextSize
}
// MARK: Media Functions
func stopMediaPlayback() {

View File

@@ -1,10 +1,10 @@
body {
margin-top: 20px;
margin-bottom: 64px;
padding-left: 64px;
padding-right: 64px;
padding-left: 48px;
padding-right: 48px;
font-family: -apple-system;
font-size: 18px;
font-size: [[font-size]]px;
}
:root {

View File

@@ -323,13 +323,16 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
NSCursor.setHiddenUntilMouseMoves(true)
// TODO: handle search mode
if timelineViewController.canGoToNextUnread() {
goToNextUnreadInTimeline()
if timelineViewController.canGoToNextUnread(wrappingToTop: false) {
goToNextUnreadInTimeline(wrappingToTop: false)
}
else if sidebarViewController.canGoToNextUnread() {
sidebarViewController.goToNextUnread()
if timelineViewController.canGoToNextUnread() {
goToNextUnreadInTimeline()
else if sidebarViewController.canGoToNextUnread(wrappingToTop: true) {
sidebarViewController.goToNextUnread(wrappingToTop: true)
// If we ended up on the same timelineViewController, we may need to wrap
// around to the top of its contents.
if timelineViewController.canGoToNextUnread(wrappingToTop: true) {
goToNextUnreadInTimeline(wrappingToTop: true)
}
}
}
@@ -995,13 +998,13 @@ private extension MainWindowController {
// MARK: - Command Validation
func canGoToNextUnread() -> Bool {
func canGoToNextUnread(wrappingToTop wrapping: Bool = false) -> Bool {
guard let timelineViewController = currentTimelineViewController, let sidebarViewController = sidebarViewController else {
return false
}
// TODO: handle search mode
return timelineViewController.canGoToNextUnread() || sidebarViewController.canGoToNextUnread()
return timelineViewController.canGoToNextUnread(wrappingToTop: wrapping) || sidebarViewController.canGoToNextUnread(wrappingToTop: wrapping)
}
func canMarkAllAsRead() -> Bool {
@@ -1188,14 +1191,14 @@ private extension MainWindowController {
// MARK: - Misc.
func goToNextUnreadInTimeline() {
func goToNextUnreadInTimeline(wrappingToTop wrapping: Bool) {
guard let timelineViewController = currentTimelineViewController else {
return
}
if timelineViewController.canGoToNextUnread() {
timelineViewController.goToNextUnread()
if timelineViewController.canGoToNextUnread(wrappingToTop: wrapping) {
timelineViewController.goToNextUnread(wrappingToTop: wrapping)
makeTimelineViewFirstResponder()
}
}

View File

@@ -18,7 +18,8 @@ import RSCore
}
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] {
return proposedServices + SharingServicePickerDelegate.customSharingServices(for: items)
let filteredServices = proposedServices.filter { $0.menuItemTitle != "NetNewsWire" }
return filteredServices + SharingServicePickerDelegate.customSharingServices(for: items)
}
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, delegateFor sharingService: NSSharingService) -> NSSharingServiceDelegate? {

View File

@@ -294,15 +294,15 @@ protocol SidebarDelegate: class {
// MARK: - Navigation
func canGoToNextUnread() -> Bool {
if let _ = nextSelectableRowWithUnreadArticle() {
func canGoToNextUnread(wrappingToTop wrapping: Bool = false) -> Bool {
if let _ = nextSelectableRowWithUnreadArticle(wrappingToTop: wrapping) {
return true
}
return false
}
func goToNextUnread() {
guard let row = nextSelectableRowWithUnreadArticle() else {
func goToNextUnread(wrappingToTop wrapping: Bool = false) {
guard let row = nextSelectableRowWithUnreadArticle(wrappingToTop: wrapping) else {
assertionFailure("goToNextUnread called before checking if there is a next unread.")
return
}
@@ -685,26 +685,42 @@ private extension SidebarViewController {
return false
}
func nextSelectableRowWithUnreadArticle() -> Int? {
// Skip group items, because they should never be selected.
let selectedRow = outlineView.selectedRow
let numberOfRows = outlineView.numberOfRows
var row = selectedRow + 1
while (row < numberOfRows) {
if rowHasAtLeastOneUnreadArticle(row) && !rowIsGroupItem(row) {
return row
}
row += 1
func rowIsExpandedFolder(_ row: Int) -> Bool {
if let node = nodeForRow(row), outlineView.isItemExpanded(node) {
return true
}
row = 0
while (row <= selectedRow) {
if rowHasAtLeastOneUnreadArticle(row) && !rowIsGroupItem(row) {
return false
}
func shouldSkipRow(_ row: Int) -> Bool {
let skipExpandedFolders = UserDefaults.standard.bool(forKey: "JalkutRespectFolderExpansionOnNextUnread")
// Skip group items, because they should never be selected.
// Skip expanded folders only if Jalkut's pref is enabled.
if rowIsGroupItem(row) || (skipExpandedFolders && rowIsExpandedFolder(row)) {
return true
}
return false
}
func nextSelectableRowWithUnreadArticle(wrappingToTop wrapping: Bool = false) -> Int? {
let numberOfRows = outlineView.numberOfRows
let startRow = outlineView.selectedRow + 1
let orderedRows: [Int]
if startRow == numberOfRows {
// Last item is selected, so start at the beginning if we allow wrapping
orderedRows = wrapping ? Array(0..<numberOfRows) : []
} else {
// Start at the selection and wrap around to the beginning
orderedRows = Array(startRow..<numberOfRows) + (wrapping ? Array(0..<startRow) : [])
}
for row in orderedRows {
// Skip group items, because they should never be selected.
if rowHasAtLeastOneUnreadArticle(row) && !shouldSkipRow(row) {
return row
}
row += 1
}
return nil

View File

@@ -10,6 +10,11 @@ import AppKit
import Articles
import RSCore
extension Article: PasteboardWriterOwner {
public var pasteboardWriter: NSPasteboardWriting {
return ArticlePasteboardWriter(article: self)
}
}
@objc final class ArticlePasteboardWriter: NSObject, NSPasteboardWriting {

View File

@@ -549,7 +549,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
tableView.scrollTo(row: ix)
}
func goToNextUnread() {
func goToNextUnread(wrappingToTop wrapping: Bool = false) {
guard let ix = indexOfNextUnreadArticle() else {
return
}
@@ -558,15 +558,15 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
tableView.scrollTo(row: ix)
}
func canGoToNextUnread() -> Bool {
guard let _ = indexOfNextUnreadArticle() else {
func canGoToNextUnread(wrappingToTop wrapping: Bool = false) -> Bool {
guard let _ = indexOfNextUnreadArticle(wrappingToTop: wrapping) else {
return false
}
return true
}
func indexOfNextUnreadArticle() -> Int? {
return articles.rowOfNextUnreadArticle(tableView.selectedRow)
func indexOfNextUnreadArticle(wrappingToTop wrapping: Bool = false) -> Int? {
return articles.rowOfNextUnreadArticle(tableView.selectedRow, wrappingToTop: wrapping)
}
func focus() {

View File

@@ -17,40 +17,14 @@
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="398" height="205"/>
<rect key="contentRect" x="196" y="240" width="398" height="135"/>
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
<view key="contentView" wantsLayer="YES" misplaced="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="398" height="205"/>
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="398" height="135"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uDK-ji-zlT">
<rect key="frame" x="127" y="107" width="145" height="38"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ySx-qg-WbT">
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
<constraints>
<constraint firstAttribute="width" constant="36" id="BKI-n8-fbR"/>
<constraint firstAttribute="height" constant="36" id="NLk-V3-hn9"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountCloudKit" id="9RZ-J3-ioX"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="80D-3X-rL2">
<rect key="frame" x="53" y="0.0" width="94" height="38"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="iCloud" id="1d2-Mx-TKe">
<font key="font" metaFont="system" size="32"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" ambiguous="YES" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uDK-ji-zlT">
<rect key="frame" x="199" y="59" width="0.0" height="16"/>
</stackView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xqo-gP-MPl">
<rect key="frame" x="310" y="13" width="75" height="32"/>
@@ -78,34 +52,56 @@ Gw
<action selector="cancel:" target="-2" id="uDi-hX-dXu"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Kj-Cl-FJQ">
<rect key="frame" x="47" y="56" width="304" height="16"/>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ySx-qg-WbT">
<rect key="frame" x="20" y="71" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="300" id="xiK-wa-r3v"/>
<constraint firstAttribute="width" constant="50" id="LlV-kM-gHE"/>
<constraint firstAttribute="height" constant="50" id="MjL-TR-9ey"/>
</constraints>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" title="This account syncs using your iCloud account." id="wfr-Dv-UIF">
<font key="font" metaFont="system"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountCloudKit" id="9RZ-J3-ioX"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="80D-3X-rL2">
<rect key="frame" x="76" y="99" width="304" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Sign in to your iCloud account." id="1d2-Mx-TKe">
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Kj-Cl-FJQ">
<rect key="frame" x="76" y="59" width="304" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="ZJN-cn-p1x"/>
</constraints>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" title="This account syncs across your Mac and iOS devices using your iCloud account." id="wfr-Dv-UIF">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="9eG-vV-s8c" firstAttribute="top" secondItem="3Kj-Cl-FJQ" secondAttribute="bottom" constant="16" id="BZP-Fw-AVu"/>
<constraint firstItem="80D-3X-rL2" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" symbolic="YES" id="4kd-dG-z7L"/>
<constraint firstItem="ySx-qg-WbT" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" symbolic="YES" id="9lr-cs-zBS"/>
<constraint firstItem="80D-3X-rL2" firstAttribute="leading" secondItem="ySx-qg-WbT" secondAttribute="trailing" constant="8" symbolic="YES" id="Ab3-E3-UxG"/>
<constraint firstItem="9eG-vV-s8c" firstAttribute="centerY" secondItem="xqo-gP-MPl" secondAttribute="centerY" id="DLC-C8-enC"/>
<constraint firstAttribute="bottom" secondItem="xqo-gP-MPl" secondAttribute="bottom" constant="20" id="HgX-ud-184"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="centerX" secondItem="EiT-Mj-1SZ" secondAttribute="centerX" id="bfd-35-bLi"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="top" secondItem="80D-3X-rL2" secondAttribute="bottom" constant="35" id="gh3-I9-x4r"/>
<constraint firstItem="80D-3X-rL2" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="16" id="h30-eN-6VF"/>
<constraint firstAttribute="trailing" secondItem="3Kj-Cl-FJQ" secondAttribute="trailing" constant="20" symbolic="YES" id="QS1-QM-lOc"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="leading" secondItem="ySx-qg-WbT" secondAttribute="trailing" constant="8" symbolic="YES" id="Ue2-Kp-nGw"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="44" id="V0G-KC-FTs"/>
<constraint firstItem="9eG-vV-s8c" firstAttribute="top" secondItem="3Kj-Cl-FJQ" secondAttribute="bottom" constant="19" id="VYR-uJ-Qmy"/>
<constraint firstItem="xqo-gP-MPl" firstAttribute="top" secondItem="3Kj-Cl-FJQ" secondAttribute="bottom" constant="19" id="mg9-pV-zid"/>
<constraint firstItem="xqo-gP-MPl" firstAttribute="leading" secondItem="9eG-vV-s8c" secondAttribute="trailing" constant="11" id="p6M-6b-ybu"/>
<constraint firstItem="uDK-ji-zlT" firstAttribute="centerX" secondItem="EiT-Mj-1SZ" secondAttribute="centerX" id="uXu-yF-PBG"/>
<constraint firstItem="ySx-qg-WbT" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="14" id="xee-QN-iej"/>
<constraint firstAttribute="trailing" secondItem="80D-3X-rL2" secondAttribute="trailing" constant="20" symbolic="YES" id="xzC-kN-UlB"/>
<constraint firstAttribute="trailing" secondItem="xqo-gP-MPl" secondAttribute="trailing" constant="20" id="z4O-vd-ear"/>
</constraints>
</view>
<connections>
<outlet property="delegate" destination="-2" id="hpu-h7-p4Z"/>
</connections>
<point key="canvasLocation" x="99" y="114.5"/>
<point key="canvasLocation" x="99" y="79.5"/>
</window>
</objects>
<resources>

View File

@@ -20,70 +20,15 @@
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="398" height="205"/>
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
<view key="contentView" wantsLayer="YES" misplaced="YES" id="EiT-Mj-1SZ">
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
<rect key="frame" x="0.0" y="0.0" width="398" height="205"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uDK-ji-zlT">
<rect key="frame" x="93" y="160" width="213" height="38"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ySx-qg-WbT">
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
<constraints>
<constraint firstAttribute="width" constant="36" id="BKI-n8-fbR"/>
<constraint firstAttribute="height" constant="36" id="NLk-V3-hn9"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountLocal" id="9RZ-J3-ioX"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="80D-3X-rL2">
<rect key="frame" x="53" y="0.0" width="162" height="38"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="On My Mac" id="1d2-Mx-TKe">
<font key="font" metaFont="system" size="32"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<rect key="frame" x="199" y="151" width="0.0" height="102"/>
</stackView>
<stackView distribution="fill" orientation="horizontal" alignment="firstBaseline" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6Q7-nI-h5u">
<rect key="frame" x="105" y="104" width="188" height="21"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MWg-UL-xtj">
<rect key="frame" x="-2" y="3" width="44" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Name:" id="AVh-ns-LEN">
<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>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="f9b-FM-i8Q">
<rect key="frame" x="48" y="0.0" width="140" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="140" id="cdf-tS-vno"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="Y12-ua-Bj8">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<rect key="frame" x="199" y="95" width="0.0" height="21"/>
</stackView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xqo-gP-MPl">
<rect key="frame" x="310" y="13" width="75" height="32"/>
@@ -111,29 +56,75 @@ Gw
<action selector="cancel:" target="-2" id="uDi-hX-dXu"/>
</connections>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Kj-Cl-FJQ">
<rect key="frame" x="87" y="56" width="224" height="32"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="80D-3X-rL2">
<rect key="frame" x="87" y="169" width="293" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Create a local account on your Mac." id="1d2-Mx-TKe">
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ySx-qg-WbT">
<rect key="frame" x="20" y="135" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="220" id="xiK-wa-r3v"/>
<constraint firstAttribute="height" constant="50" id="Hzy-OP-e84"/>
<constraint firstAttribute="width" constant="50" id="Z8v-O8-FE5"/>
</constraints>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" title="This account stores all of its data on your Mac. It does not sync." id="wfr-Dv-UIF">
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountLocal" id="9RZ-J3-ioX"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MWg-UL-xtj">
<rect key="frame" x="87" y="98" width="44" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Name:" id="AVh-ns-LEN">
<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>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="f9b-FM-i8Q">
<rect key="frame" x="137" y="95" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="iff-tN-Gkx"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="Y12-ua-Bj8">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Kj-Cl-FJQ">
<rect key="frame" x="87" y="129" width="293" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="CaZ-mY-r4X"/>
</constraints>
<textFieldCell key="cell" selectable="YES" allowsUndo="NO" title="Local accounts store their data on your Mac. They do not sync across your devices." id="wfr-Dv-UIF">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="6Q7-nI-h5u" firstAttribute="top" secondItem="80D-3X-rL2" secondAttribute="bottom" constant="35" id="3Sa-lr-BqF"/>
<constraint firstItem="9eG-vV-s8c" firstAttribute="top" secondItem="3Kj-Cl-FJQ" secondAttribute="bottom" constant="16" id="BZP-Fw-AVu"/>
<constraint firstAttribute="bottom" secondItem="uDK-ji-zlT" secondAttribute="bottom" constant="151" id="14A-2i-FXI"/>
<constraint firstItem="f9b-FM-i8Q" firstAttribute="top" secondItem="3Kj-Cl-FJQ" secondAttribute="bottom" constant="13" id="6xW-9w-pGi"/>
<constraint firstItem="9eG-vV-s8c" firstAttribute="centerY" secondItem="xqo-gP-MPl" secondAttribute="centerY" id="DLC-C8-enC"/>
<constraint firstAttribute="trailing" secondItem="f9b-FM-i8Q" secondAttribute="trailing" constant="61" id="Fgt-dR-DTr"/>
<constraint firstAttribute="bottom" secondItem="xqo-gP-MPl" secondAttribute="bottom" constant="20" id="HgX-ud-184"/>
<constraint firstItem="uDK-ji-zlT" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="-48" id="JWY-Q9-7sR"/>
<constraint firstItem="6Q7-nI-h5u" firstAttribute="centerX" secondItem="EiT-Mj-1SZ" secondAttribute="centerX" id="KYO-G6-21s"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="top" secondItem="6Q7-nI-h5u" secondAttribute="bottom" constant="16" id="baJ-uI-EGJ"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="centerX" secondItem="EiT-Mj-1SZ" secondAttribute="centerX" id="bfd-35-bLi"/>
<constraint firstItem="80D-3X-rL2" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="16" id="h30-eN-6VF"/>
<constraint firstItem="80D-3X-rL2" firstAttribute="leading" secondItem="ySx-qg-WbT" secondAttribute="trailing" constant="19" id="Kuc-ym-H5a"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="leading" secondItem="ySx-qg-WbT" secondAttribute="trailing" constant="19" id="Pwn-uQ-qtc"/>
<constraint firstItem="3Kj-Cl-FJQ" firstAttribute="top" secondItem="80D-3X-rL2" secondAttribute="bottom" constant="8" symbolic="YES" id="Rcn-2y-Jkt"/>
<constraint firstItem="f9b-FM-i8Q" firstAttribute="bottom" secondItem="6Q7-nI-h5u" secondAttribute="bottom" id="Udo-Yg-xOC"/>
<constraint firstItem="ySx-qg-WbT" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" constant="20" symbolic="YES" id="cSC-er-N4i"/>
<constraint firstItem="ySx-qg-WbT" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" symbolic="YES" id="dmH-e1-X3W"/>
<constraint firstItem="f9b-FM-i8Q" firstAttribute="top" secondItem="6Q7-nI-h5u" secondAttribute="top" id="neW-V1-13e"/>
<constraint firstItem="f9b-FM-i8Q" firstAttribute="leading" secondItem="MWg-UL-xtj" secondAttribute="trailing" constant="8" symbolic="YES" id="p3J-6S-ITL"/>
<constraint firstItem="xqo-gP-MPl" firstAttribute="leading" secondItem="9eG-vV-s8c" secondAttribute="trailing" constant="11" id="p6M-6b-ybu"/>
<constraint firstItem="f9b-FM-i8Q" firstAttribute="baseline" secondItem="MWg-UL-xtj" secondAttribute="baseline" id="pz0-6G-uO5"/>
<constraint firstItem="80D-3X-rL2" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" constant="20" symbolic="YES" id="qaR-ST-LnB"/>
<constraint firstItem="uDK-ji-zlT" firstAttribute="centerX" secondItem="EiT-Mj-1SZ" secondAttribute="centerX" id="uXu-yF-PBG"/>
<constraint firstAttribute="trailing" secondItem="80D-3X-rL2" secondAttribute="trailing" constant="20" symbolic="YES" id="xkO-OB-uR2"/>
<constraint firstAttribute="trailing" secondItem="3Kj-Cl-FJQ" secondAttribute="trailing" constant="20" symbolic="YES" id="z0z-oa-qLm"/>
<constraint firstAttribute="trailing" secondItem="xqo-gP-MPl" secondAttribute="trailing" constant="20" id="z4O-vd-ear"/>
</constraints>
</view>

View File

@@ -23,7 +23,7 @@ class AccountsAddLocalWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
localAccountNameTextField.stringValue = Account.defaultLocalAccountName
localAccountNameTextField.stringValue = NSLocalizedString("Create a local account on your Mac.", comment: "Account Local")
}
// MARK: API

View File

@@ -3,15 +3,19 @@
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AccountsFeedWranglerWindowController" customModule="NetNewsWire" customModuleProvider="target">
<connections>
<outlet property="actionButton" destination="9mz-D9-krh" id="ozu-6Q-9Lb"/>
<outlet property="errorMessageLabel" destination="byK-Sd-r7F" id="8zt-9d-dWQ"/>
<outlet property="createNewAccountButton" destination="pPT-Cj-3vI" id="KAL-Y7-XQK"/>
<outlet property="errorMessageLabel" destination="zwG-Ag-z8o" id="7a1-iJ-URN"/>
<outlet property="noAccountTextField" destination="xEl-Ae-5r8" id="dU3-Jv-Aq8"/>
<outlet property="passwordTextField" destination="JSa-LY-zNQ" id="5cF-bM-CJE"/>
<outlet property="progressIndicator" destination="B0W-bh-Evv" id="Tiq-gx-s3F"/>
<outlet property="signInTextField" destination="lti-yM-8LV" id="ZgR-2i-RXB"/>
<outlet property="usernameTextField" destination="78p-Cf-f55" id="Gg5-Ce-RJv"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
@@ -28,40 +32,13 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7Ht-Fn-0Ya">
<rect key="frame" x="89" y="191" width="256" height="38"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
<constraints>
<constraint firstAttribute="height" constant="36" id="Ern-Kk-8LX"/>
<constraint firstAttribute="width" constant="36" id="PLS-68-NMc"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountFeedWrangler" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="53" y="0.0" width="205" height="38"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Feed Wrangler" id="ras-dj-nP8">
<font key="font" metaFont="system" size="32"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<rect key="frame" x="217" y="229" width="0.0" height="0.0"/>
</stackView>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="12" columnSpacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="79" y="60" width="276" height="99"/>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="7" columnSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="80" y="127" width="270" height="49"/>
<rows>
<gridRow id="DRl-lC-vUc"/>
<gridRow id="eW8-uH-txq"/>
<gridRow id="DbI-7g-Xme"/>
</rows>
<columns>
<gridColumn id="fCQ-jY-Mts"/>
@@ -70,7 +47,7 @@
<gridCells>
<gridCell row="DRl-lC-vUc" column="fCQ-jY-Mts" id="4DI-01-jGD">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Zy6-9c-8TI">
<rect key="frame" x="23" y="81" width="41" height="16"/>
<rect key="frame" x="23" y="31" width="41" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Email:" id="DqN-SV-v35">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -80,11 +57,11 @@
</gridCell>
<gridCell row="DRl-lC-vUc" column="7CY-bX-6x4" id="Z0b-qS-MUJ">
<textField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="78p-Cf-f55">
<rect key="frame" x="76" y="78" width="200" height="21"/>
<rect key="frame" x="70" y="28" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="Qin-jm-4zt"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="fCk-Tf-q01">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="me@email.com" drawsBackground="YES" id="fCk-Tf-q01">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -93,7 +70,7 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="fCQ-jY-Mts" id="Hqa-3w-cQv">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wEx-TM-rPM">
<rect key="frame" x="-2" y="48" width="66" height="16"/>
<rect key="frame" x="-2" y="3" width="66" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Password:" id="7g8-Kk-ISg">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -103,11 +80,11 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="7CY-bX-6x4" id="m16-3v-9pf">
<secureTextField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JSa-LY-zNQ">
<rect key="frame" x="76" y="45" width="200" height="21"/>
<rect key="frame" x="70" y="0.0" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="eal-wa-1nU"/>
</constraints>
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="•••••••••" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -117,17 +94,6 @@
</secureTextFieldCell>
</secureTextField>
</gridCell>
<gridCell row="DbI-7g-Xme" column="fCQ-jY-Mts" headOfMergedCell="xX0-vn-AId" xPlacement="leading" id="xX0-vn-AId">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="byK-Sd-r7F">
<rect key="frame" x="-2" y="9" width="104" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" id="0yh-Ab-UTX">
<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>
</gridCell>
<gridCell row="DbI-7g-Xme" column="7CY-bX-6x4" headOfMergedCell="xX0-vn-AId" id="hk5-St-E4y"/>
</gridCells>
</gridView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9mz-D9-krh">
@@ -156,22 +122,100 @@ Gw
<action selector="cancel:" target="-2" id="WAD-ES-hpq"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="20" y="179" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="Ern-Kk-8LX"/>
<constraint firstAttribute="width" constant="50" id="PLS-68-NMc"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountFeedWrangler" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="78" y="213" width="337" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Sign in to your Feed Wrangler account." id="ras-dj-nP8">
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xEl-Ae-5r8">
<rect key="frame" x="78" y="193" width="216" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Don't have a Feed Wrangler account?" id="DFR-20-sjO">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pPT-Cj-3vI">
<rect key="frame" x="293" y="193" width="105" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="105" id="Ez2-gz-Wqf"/>
</constraints>
<buttonCell key="cell" type="roundRect" title="Create one here." bezelStyle="roundedRect" alignment="center" state="on" imageScaling="proportionallyDown" inset="2" id="tlF-nc-ZOr">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
</buttonCell>
<color key="contentTintColor" name="AccentColor"/>
<connections>
<action selector="createAccountWithProvider:" target="-2" id="bp5-3n-RLW"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Uzn-QS-o4p">
<rect key="frame" x="78" y="73" width="337" height="39"/>
<constraints>
<constraint firstAttribute="height" constant="39" id="99q-po-zLV"/>
<constraint firstAttribute="width" constant="333" id="oSi-jz-DZ8"/>
</constraints>
<textFieldCell key="cell" title="Your username and password will be encrypted and stored in Keychain. " id="83j-VH-GgC">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="B0W-bh-Evv">
<rect key="frame" x="209" y="167" width="16" height="16"/>
<rect key="frame" x="256" y="22" width="16" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="16" id="ggl-Gq-PUV"/>
<constraint firstAttribute="height" constant="16" id="m1z-4y-g41"/>
</constraints>
</progressIndicator>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zwG-Ag-z8o">
<rect key="frame" x="18" y="23" width="230" height="14"/>
<constraints>
<constraint firstAttribute="width" constant="226" id="jvg-qt-4RV"/>
</constraints>
<textFieldCell key="cell" id="b2G-2g-1KR">
<font key="font" textStyle="subheadline" name=".SFNS-Regular"/>
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="3dK-9R-7wX"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="8qB-Qh-zBJ"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="leading" secondItem="XAM-Hb-0Hw" secondAttribute="trailing" constant="12" symbolic="YES" id="CC8-HR-FDy"/>
<constraint firstItem="xEl-Ae-5r8" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="5" id="FOT-OS-h0G"/>
<constraint firstAttribute="trailing" secondItem="lti-yM-8LV" secondAttribute="trailing" constant="20" symbolic="YES" id="Hxs-l1-XFt"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="leading" secondItem="zwG-Ag-z8o" secondAttribute="trailing" constant="10" id="Kyv-Pw-z56"/>
<constraint firstItem="Uzn-QS-o4p" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="Lm2-GS-vEg"/>
<constraint firstItem="XAM-Hb-0Hw" firstAttribute="centerY" secondItem="9mz-D9-krh" secondAttribute="centerY" id="M2M-fb-kfR"/>
<constraint firstItem="pPT-Cj-3vI" firstAttribute="leading" secondItem="xEl-Ae-5r8" secondAttribute="trailing" constant="1" id="NXU-SK-5WO"/>
<constraint firstAttribute="bottom" secondItem="9mz-D9-krh" secondAttribute="bottom" constant="20" id="PK2-Ye-400"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="B0W-bh-Evv" secondAttribute="bottom" constant="8" id="V7z-a7-OOG"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="top" secondItem="zBB-JH-huI" secondAttribute="bottom" constant="20" symbolic="YES" id="Wu3-hp-Vzh"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="aFI-4s-mMv"/>
<constraint firstItem="pPT-Cj-3vI" firstAttribute="centerY" secondItem="xEl-Ae-5r8" secondAttribute="centerY" id="XKR-hU-WpE"/>
<constraint firstItem="Uzn-QS-o4p" firstAttribute="top" secondItem="JSa-LY-zNQ" secondAttribute="bottom" constant="15" id="Z84-bl-0dI"/>
<constraint firstItem="XAM-Hb-0Hw" firstAttribute="leading" secondItem="B0W-bh-Evv" secondAttribute="trailing" constant="8" symbolic="YES" id="afl-Cl-zda"/>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="dDr-Rs-AyZ"/>
<constraint firstAttribute="bottom" secondItem="B0W-bh-Evv" secondAttribute="bottom" constant="22" id="dzj-Jm-8mI"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="eIn-Pl-krd"/>
<constraint firstItem="zwG-Ag-z8o" firstAttribute="centerY" secondItem="XAM-Hb-0Hw" secondAttribute="centerY" id="ed6-s2-xPp"/>
<constraint firstAttribute="trailing" secondItem="9mz-D9-krh" secondAttribute="trailing" constant="20" id="fVQ-zN-rKd"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="8" id="gq2-tB-pXH"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" id="jlY-Jg-KJR"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="lrN-Gd-iXd"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="xEl-Ae-5r8" secondAttribute="bottom" constant="17" id="rbr-SG-72y"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="tAZ-Te-w3H"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="wWG-kT-6M7"/>
<constraint firstItem="xEl-Ae-5r8" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="zAY-I9-eKa"/>
<constraint firstItem="zwG-Ag-z8o" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="zDh-um-1B7"/>
</constraints>
</view>
<connections>
@@ -182,5 +226,8 @@ Gw
</objects>
<resources>
<image name="accountFeedWrangler" width="261" height="261"/>
<namedColor name="AccentColor">
<color red="0.030999999493360519" green="0.41600000858306885" blue="0.93300002813339233" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@@ -12,6 +12,10 @@ import RSWeb
import Secrets
class AccountsFeedWranglerWindowController: NSWindowController {
@IBOutlet weak var signInTextField: NSTextField!
@IBOutlet weak var noAccountTextField: NSTextField!
@IBOutlet weak var createNewAccountButton: NSButton!
@IBOutlet weak var progressIndicator: NSProgressIndicator!
@IBOutlet weak var usernameTextField: NSTextField!
@IBOutlet weak var passwordTextField: NSSecureTextField!
@@ -30,8 +34,12 @@ class AccountsFeedWranglerWindowController: NSWindowController {
if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) {
usernameTextField.stringValue = credentials.username
actionButton.title = NSLocalizedString("Update", comment: "Update")
signInTextField.stringValue = NSLocalizedString("Update your Feed Wrangler account credentials.", comment: "SignIn")
noAccountTextField.isHidden = true
createNewAccountButton.isHidden = true
} else {
actionButton.title = NSLocalizedString("Create", comment: "Create")
signInTextField.stringValue = NSLocalizedString("Sign in to your Feed Wrangler account.", comment: "SignIn")
}
}
@@ -113,4 +121,9 @@ class AccountsFeedWranglerWindowController: NSWindowController {
}
}
}
@IBAction func createAccountWithProvider(_ sender: Any) {
NSWorkspace.shared.open(URL(string: "https://feedwrangler.net/users/new")!)
}
}

View File

@@ -3,15 +3,19 @@
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AccountsFeedbinWindowController" customModule="NetNewsWire" customModuleProvider="target">
<connections>
<outlet property="actionButton" destination="9mz-D9-krh" id="ozu-6Q-9Lb"/>
<outlet property="errorMessageLabel" destination="byK-Sd-r7F" id="8zt-9d-dWQ"/>
<outlet property="createNewAccountButton" destination="MlB-jN-Ybe" id="w6v-BW-0yQ"/>
<outlet property="errorMessageLabel" destination="sPC-hi-ewD" id="Lb6-0x-X2t"/>
<outlet property="noAccountTextField" destination="gEK-TH-URn" id="Spi-K5-FmU"/>
<outlet property="passwordTextField" destination="JSa-LY-zNQ" id="5cF-bM-CJE"/>
<outlet property="progressIndicator" destination="B0W-bh-Evv" id="Tiq-gx-s3F"/>
<outlet property="signInTextField" destination="lti-yM-8LV" id="xU2-4q-bP7"/>
<outlet property="usernameTextField" destination="78p-Cf-f55" id="Gg5-Ce-RJv"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
@@ -28,43 +32,13 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7Ht-Fn-0Ya">
<rect key="frame" x="134" y="191" width="166" height="38"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
<constraints>
<constraint firstAttribute="height" constant="36" id="Ern-Kk-8LX"/>
<constraint firstAttribute="width" constant="36" id="PLS-68-NMc"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountFeedbin" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="53" y="0.0" width="115" height="38"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Feedbin" id="ras-dj-nP8">
<font key="font" metaFont="system" size="32"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<rect key="frame" x="233" y="229" width="0.0" height="0.0"/>
</stackView>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="12" columnSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="82" y="60" width="270" height="99"/>
<constraints>
<constraint firstItem="byK-Sd-r7F" firstAttribute="width" secondItem="JSa-LY-zNQ" secondAttribute="width" id="ImZ-BU-uKB"/>
</constraints>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="7" columnSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="80" y="127" width="270" height="49"/>
<rows>
<gridRow id="DRl-lC-vUc"/>
<gridRow id="eW8-uH-txq"/>
<gridRow id="DbI-7g-Xme"/>
</rows>
<columns>
<gridColumn id="fCQ-jY-Mts"/>
@@ -73,7 +47,7 @@
<gridCells>
<gridCell row="DRl-lC-vUc" column="fCQ-jY-Mts" id="4DI-01-jGD">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Zy6-9c-8TI">
<rect key="frame" x="23" y="81" width="41" height="16"/>
<rect key="frame" x="23" y="31" width="41" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Email:" id="DqN-SV-v35">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -83,11 +57,11 @@
</gridCell>
<gridCell row="DRl-lC-vUc" column="7CY-bX-6x4" id="Z0b-qS-MUJ">
<textField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="78p-Cf-f55">
<rect key="frame" x="70" y="78" width="200" height="21"/>
<rect key="frame" x="70" y="28" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="Qin-jm-4zt"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="fCk-Tf-q01">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="me@email.com" drawsBackground="YES" id="fCk-Tf-q01">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -96,7 +70,7 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="fCQ-jY-Mts" id="Hqa-3w-cQv">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wEx-TM-rPM">
<rect key="frame" x="-2" y="48" width="66" height="16"/>
<rect key="frame" x="-2" y="3" width="66" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Password:" id="7g8-Kk-ISg">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -106,11 +80,11 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="7CY-bX-6x4" id="m16-3v-9pf">
<secureTextField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JSa-LY-zNQ">
<rect key="frame" x="70" y="45" width="200" height="21"/>
<rect key="frame" x="70" y="0.0" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="eal-wa-1nU"/>
</constraints>
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="•••••••••" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -120,17 +94,6 @@
</secureTextFieldCell>
</secureTextField>
</gridCell>
<gridCell row="DbI-7g-Xme" column="fCQ-jY-Mts" xPlacement="leading" id="xX0-vn-AId"/>
<gridCell row="DbI-7g-Xme" column="7CY-bX-6x4" yPlacement="top" id="hk5-St-E4y">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="byK-Sd-r7F">
<rect key="frame" x="68" y="17" width="204" height="16"/>
<textFieldCell key="cell" id="0yh-Ab-UTX">
<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>
</gridCell>
</gridCells>
</gridView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9mz-D9-krh">
@@ -159,22 +122,100 @@ Gw
<action selector="cancel:" target="-2" id="WAD-ES-hpq"/>
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="20" y="182" width="50" height="50"/>
<constraints>
<constraint firstAttribute="width" constant="50" id="C4r-4y-79Z"/>
<constraint firstAttribute="height" constant="50" id="prg-y8-RI9"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountFeedbin" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="78" y="213" width="337" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Sign in to your Feedbin account." id="ras-dj-nP8">
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gEK-TH-URn">
<rect key="frame" x="78" y="193" width="179" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Don't have a Feedbin account?" id="QF5-gl-fjb">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="X1Z-v1-T5L">
<rect key="frame" x="78" y="82" width="337" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="NRl-GS-cHS"/>
<constraint firstAttribute="width" constant="333" id="cNd-Nt-bdB"/>
</constraints>
<textFieldCell key="cell" title="Your username and password will be encrypted and stored in Keychain. " id="HuQ-TS-tS3">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MlB-jN-Ybe">
<rect key="frame" x="254" y="193" width="105" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="105" id="fHd-fN-n8G"/>
</constraints>
<buttonCell key="cell" type="roundRect" title="Create one here." bezelStyle="roundedRect" alignment="center" state="on" imageScaling="proportionallyDown" inset="2" id="fx3-Uk-NVw">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
</buttonCell>
<color key="contentTintColor" name="AccentColor"/>
<connections>
<action selector="createAccountWithProvider:" target="-2" id="t3r-Jo-xim"/>
</connections>
</button>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="B0W-bh-Evv">
<rect key="frame" x="209" y="167" width="16" height="16"/>
<rect key="frame" x="256" y="22" width="16" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="16" id="Tcz-vj-cLr"/>
<constraint firstAttribute="height" constant="16" id="slt-7y-Jw0"/>
</constraints>
</progressIndicator>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="sPC-hi-ewD">
<rect key="frame" x="27" y="23" width="223" height="14"/>
<constraints>
<constraint firstAttribute="width" constant="219" id="1jw-Q0-cm5"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" id="sl9-fG-jwg">
<font key="font" textStyle="subheadline" name=".SFNS-Regular"/>
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="X1Z-v1-T5L" firstAttribute="top" secondItem="JSa-LY-zNQ" secondAttribute="bottom" constant="15" id="2U1-Zm-vzz"/>
<constraint firstItem="gEK-TH-URn" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="2UN-2R-QiC"/>
<constraint firstItem="gEK-TH-URn" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="5" id="6gj-jv-thI"/>
<constraint firstAttribute="trailing" secondItem="lti-yM-8LV" secondAttribute="trailing" constant="20" symbolic="YES" id="Bvl-lx-VMh"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="leading" secondItem="XAM-Hb-0Hw" secondAttribute="trailing" constant="12" symbolic="YES" id="CC8-HR-FDy"/>
<constraint firstItem="sPC-hi-ewD" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="29" id="DFv-ba-n2r"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="DGC-SC-Klr"/>
<constraint firstItem="sPC-hi-ewD" firstAttribute="centerY" secondItem="XAM-Hb-0Hw" secondAttribute="centerY" id="GAG-BE-Cdn"/>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="HFJ-XG-KGU"/>
<constraint firstItem="XAM-Hb-0Hw" firstAttribute="centerY" secondItem="9mz-D9-krh" secondAttribute="centerY" id="M2M-fb-kfR"/>
<constraint firstAttribute="bottom" secondItem="9mz-D9-krh" secondAttribute="bottom" constant="20" id="PK2-Ye-400"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="B0W-bh-Evv" secondAttribute="bottom" constant="8" id="V7z-a7-OOG"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="top" secondItem="zBB-JH-huI" secondAttribute="bottom" constant="20" symbolic="YES" id="Wu3-hp-Vzh"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="aFI-4s-mMv"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="centerY" secondItem="XAM-Hb-0Hw" secondAttribute="centerY" id="YYi-d9-Ykb"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="a2m-T4-Cvm"/>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="17" id="er0-pf-nSm"/>
<constraint firstAttribute="trailing" secondItem="9mz-D9-krh" secondAttribute="trailing" constant="20" id="fVQ-zN-rKd"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="8" id="gq2-tB-pXH"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" id="jlY-Jg-KJR"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="lrN-Gd-iXd"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="tAZ-Te-w3H"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="leading" secondItem="sPC-hi-ewD" secondAttribute="trailing" constant="8" symbolic="YES" id="jvz-NC-C9Q"/>
<constraint firstItem="MlB-jN-Ybe" firstAttribute="leading" secondItem="gEK-TH-URn" secondAttribute="trailing" constant="-1" id="o73-Mf-NOJ"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="gEK-TH-URn" secondAttribute="bottom" constant="17" id="rZU-JI-MfQ"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" constant="16.5" id="tAZ-Te-w3H"/>
<constraint firstItem="XAM-Hb-0Hw" firstAttribute="leading" secondItem="B0W-bh-Evv" secondAttribute="trailing" constant="8" symbolic="YES" id="uTp-NW-oDs"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="uzf-L9-WGW"/>
<constraint firstItem="X1Z-v1-T5L" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="wEv-sV-yfy"/>
<constraint firstItem="MlB-jN-Ybe" firstAttribute="centerY" secondItem="gEK-TH-URn" secondAttribute="centerY" id="xKz-dq-g9H"/>
</constraints>
</view>
<connections>
@@ -185,5 +226,8 @@ Gw
</objects>
<resources>
<image name="accountFeedbin" width="369" height="343"/>
<namedColor name="AccentColor">
<color red="0.030999999493360519" green="0.41600000858306885" blue="0.93300002813339233" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@@ -13,6 +13,9 @@ import Secrets
class AccountsFeedbinWindowController: NSWindowController {
@IBOutlet weak var signInTextField: NSTextField!
@IBOutlet weak var noAccountTextField: NSTextField!
@IBOutlet weak var createNewAccountButton: NSButton!
@IBOutlet weak var progressIndicator: NSProgressIndicator!
@IBOutlet weak var usernameTextField: NSTextField!
@IBOutlet weak var passwordTextField: NSSecureTextField!
@@ -31,8 +34,12 @@ class AccountsFeedbinWindowController: NSWindowController {
if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) {
usernameTextField.stringValue = credentials.username
actionButton.title = NSLocalizedString("Update", comment: "Update")
signInTextField.stringValue = NSLocalizedString("Update your Feedbin account credentials.", comment: "SignIn")
noAccountTextField.isHidden = true
createNewAccountButton.isHidden = true
} else {
actionButton.title = NSLocalizedString("Add Account", comment: "Add Account")
actionButton.title = NSLocalizedString("Create", comment: "Add Account")
signInTextField.stringValue = NSLocalizedString("Sign in to your Feedbin account.", comment: "SignIn")
}
}
@@ -117,5 +124,10 @@ class AccountsFeedbinWindowController: NSWindowController {
}
}
@IBAction func createAccountWithProvider(_ sender: Any) {
NSWorkspace.shared.open(URL(string: "https://feedbin.com/signup")!)
}
}

View File

@@ -3,15 +3,19 @@
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AccountsNewsBlurWindowController" customModule="NetNewsWire" customModuleProvider="target">
<connections>
<outlet property="actionButton" destination="9mz-D9-krh" id="ozu-6Q-9Lb"/>
<outlet property="errorMessageLabel" destination="byK-Sd-r7F" id="8zt-9d-dWQ"/>
<outlet property="createNewAccountButton" destination="9oE-Gq-qp7" id="95R-be-GZr"/>
<outlet property="errorMessageLabel" destination="tFv-QN-zMA" id="mcs-9L-T3b"/>
<outlet property="noAccountTextField" destination="yqn-od-aLJ" id="3Az-uY-FfZ"/>
<outlet property="passwordTextField" destination="JSa-LY-zNQ" id="5cF-bM-CJE"/>
<outlet property="progressIndicator" destination="B0W-bh-Evv" id="Tiq-gx-s3F"/>
<outlet property="signInTextField" destination="lti-yM-8LV" id="mNk-mt-HZ8"/>
<outlet property="usernameTextField" destination="78p-Cf-f55" id="Gg5-Ce-RJv"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
@@ -28,40 +32,13 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7Ht-Fn-0Ya">
<rect key="frame" x="123" y="191" width="188" height="38"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
<constraints>
<constraint firstAttribute="height" constant="36" id="Ern-Kk-8LX"/>
<constraint firstAttribute="width" constant="36" id="PLS-68-NMc"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountNewsBlur" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="53" y="0.0" width="137" height="38"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="NewsBlur" id="ras-dj-nP8">
<font key="font" metaFont="system" size="32"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<rect key="frame" x="217" y="229" width="0.0" height="0.0"/>
</stackView>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="12" columnSpacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="51" y="60" width="332" height="99"/>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="7" columnSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="80" y="127" width="326" height="49"/>
<rows>
<gridRow id="DRl-lC-vUc"/>
<gridRow id="eW8-uH-txq"/>
<gridRow id="DbI-7g-Xme"/>
</rows>
<columns>
<gridColumn id="fCQ-jY-Mts"/>
@@ -70,7 +47,7 @@
<gridCells>
<gridCell row="DRl-lC-vUc" column="fCQ-jY-Mts" id="4DI-01-jGD">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Zy6-9c-8TI">
<rect key="frame" x="-2" y="81" width="122" height="16"/>
<rect key="frame" x="-2" y="31" width="122" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Username or Email:" id="DqN-SV-v35">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -80,11 +57,11 @@
</gridCell>
<gridCell row="DRl-lC-vUc" column="7CY-bX-6x4" id="Z0b-qS-MUJ">
<textField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="78p-Cf-f55">
<rect key="frame" x="132" y="78" width="200" height="21"/>
<rect key="frame" x="126" y="28" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="Qin-jm-4zt"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="fCk-Tf-q01">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="me@email.com" drawsBackground="YES" id="fCk-Tf-q01">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -93,7 +70,7 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="fCQ-jY-Mts" id="Hqa-3w-cQv">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wEx-TM-rPM">
<rect key="frame" x="54" y="48" width="66" height="16"/>
<rect key="frame" x="54" y="3" width="66" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Password:" id="7g8-Kk-ISg">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -103,11 +80,11 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="7CY-bX-6x4" id="m16-3v-9pf">
<secureTextField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JSa-LY-zNQ">
<rect key="frame" x="132" y="45" width="200" height="21"/>
<rect key="frame" x="126" y="0.0" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="eal-wa-1nU"/>
</constraints>
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="••••••••" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -117,17 +94,6 @@
</secureTextFieldCell>
</secureTextField>
</gridCell>
<gridCell row="DbI-7g-Xme" column="fCQ-jY-Mts" headOfMergedCell="xX0-vn-AId" xPlacement="leading" id="xX0-vn-AId">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="byK-Sd-r7F">
<rect key="frame" x="-2" y="9" width="104" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" id="0yh-Ab-UTX">
<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>
</gridCell>
<gridCell row="DbI-7g-Xme" column="7CY-bX-6x4" headOfMergedCell="xX0-vn-AId" id="hk5-St-E4y"/>
</gridCells>
</gridView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9mz-D9-krh">
@@ -157,21 +123,98 @@ Gw
</connections>
</button>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="B0W-bh-Evv">
<rect key="frame" x="209" y="167" width="16" height="16"/>
<rect key="frame" x="256" y="22" width="16" height="16"/>
<constraints>
<constraint firstAttribute="height" constant="16" id="Cew-Vp-UKw"/>
<constraint firstAttribute="width" constant="16" id="P6W-hb-FGm"/>
</constraints>
</progressIndicator>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="20" y="179" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="1ou-e3-F9w"/>
<constraint firstAttribute="width" constant="50" id="llp-FJ-LQl"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="accountNewsBlur" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="78" y="213" width="330" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Sign in to your NewsBlur account." id="ras-dj-nP8">
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yqn-od-aLJ">
<rect key="frame" x="78" y="193" width="187" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Don't have a NewsBlur account?" id="fPs-Sh-GEf">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9oE-Gq-qp7">
<rect key="frame" x="264" y="193" width="105" height="15"/>
<constraints>
<constraint firstAttribute="width" constant="105" id="Z91-fh-Mw5"/>
</constraints>
<buttonCell key="cell" type="roundRect" title="Create one here." bezelStyle="roundedRect" alignment="center" state="on" imageScaling="proportionallyDown" inset="2" id="moP-jO-Ubz">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="cellTitle"/>
</buttonCell>
<color key="contentTintColor" name="AccentColor"/>
<connections>
<action selector="createAccountWithProvider:" target="-2" id="FMt-SQ-TGa"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OMB-u7-kzz">
<rect key="frame" x="78" y="73" width="337" height="39"/>
<constraints>
<constraint firstAttribute="width" constant="333" id="9Bx-xl-Lf4"/>
<constraint firstAttribute="height" constant="39" id="dYs-8A-ml0"/>
</constraints>
<textFieldCell key="cell" title="Your username and password will be encrypted and stored in Keychain. " id="VWI-0W-Duk">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="tFv-QN-zMA">
<rect key="frame" x="20" y="22" width="230" height="14"/>
<constraints>
<constraint firstAttribute="width" constant="226" id="7XO-aQ-XFv"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" id="yQ5-2h-Y9i">
<font key="font" textStyle="subheadline" name=".SFNS-Regular"/>
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="tFv-QN-zMA" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="22" id="0vw-sR-47d"/>
<constraint firstItem="OMB-u7-kzz" firstAttribute="top" secondItem="JSa-LY-zNQ" secondAttribute="bottom" constant="15" id="5YM-1I-eNA"/>
<constraint firstAttribute="bottom" secondItem="tFv-QN-zMA" secondAttribute="bottom" constant="22" id="8K5-dH-2jc"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="leading" secondItem="XAM-Hb-0Hw" secondAttribute="trailing" constant="12" symbolic="YES" id="CC8-HR-FDy"/>
<constraint firstItem="yqn-od-aLJ" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="FVS-Uu-8cX"/>
<constraint firstAttribute="trailing" secondItem="lti-yM-8LV" secondAttribute="trailing" constant="27" id="J9X-fP-LuZ"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="KH1-LS-xLd"/>
<constraint firstItem="XAM-Hb-0Hw" firstAttribute="centerY" secondItem="9mz-D9-krh" secondAttribute="centerY" id="M2M-fb-kfR"/>
<constraint firstItem="yqn-od-aLJ" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="5" id="M9g-aH-poR"/>
<constraint firstAttribute="bottom" secondItem="9mz-D9-krh" secondAttribute="bottom" constant="20" id="PK2-Ye-400"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="B0W-bh-Evv" secondAttribute="bottom" constant="8" id="V7z-a7-OOG"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="top" secondItem="zBB-JH-huI" secondAttribute="bottom" constant="20" symbolic="YES" id="Wu3-hp-Vzh"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="aFI-4s-mMv"/>
<constraint firstItem="9oE-Gq-qp7" firstAttribute="centerY" secondItem="yqn-od-aLJ" secondAttribute="centerY" id="VJR-Vq-lRi"/>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="XLG-4y-Pfd"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="eNp-CL-ocA"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="yqn-od-aLJ" secondAttribute="bottom" constant="17" id="fO2-t2-I4M"/>
<constraint firstAttribute="trailing" secondItem="9mz-D9-krh" secondAttribute="trailing" constant="20" id="fVQ-zN-rKd"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="8" id="gq2-tB-pXH"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" id="jlY-Jg-KJR"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="lrN-Gd-iXd"/>
<constraint firstItem="OMB-u7-kzz" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="jm9-kY-iQy"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="kLe-eK-uhH"/>
<constraint firstItem="9oE-Gq-qp7" firstAttribute="leading" secondItem="yqn-od-aLJ" secondAttribute="trailing" constant="1" id="ks3-4Y-649"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="leading" secondItem="tFv-QN-zMA" secondAttribute="trailing" constant="8" symbolic="YES" id="oDj-tr-QWc"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="tAZ-Te-w3H"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="centerY" secondItem="XAM-Hb-0Hw" secondAttribute="centerY" id="uUt-Cg-PFc"/>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="xUF-s0-Ews"/>
</constraints>
</view>
<connections>
@@ -182,5 +225,8 @@ Gw
</objects>
<resources>
<image name="accountNewsBlur" width="25" height="25"/>
<namedColor name="AccentColor">
<color red="0.030999999493360519" green="0.41600000858306885" blue="0.93300002813339233" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@@ -12,6 +12,10 @@ import RSWeb
import Secrets
class AccountsNewsBlurWindowController: NSWindowController {
@IBOutlet weak var signInTextField: NSTextField!
@IBOutlet weak var noAccountTextField: NSTextField!
@IBOutlet weak var createNewAccountButton: NSButton!
@IBOutlet weak var progressIndicator: NSProgressIndicator!
@IBOutlet weak var usernameTextField: NSTextField!
@IBOutlet weak var passwordTextField: NSSecureTextField!
@@ -30,8 +34,12 @@ class AccountsNewsBlurWindowController: NSWindowController {
if let account = account, let credentials = try? account.retrieveCredentials(type: .newsBlurBasic) {
usernameTextField.stringValue = credentials.username
actionButton.title = NSLocalizedString("Update", comment: "Update")
signInTextField.stringValue = NSLocalizedString("Update your NewsBlur account credentials.", comment: "SignIn")
noAccountTextField.isHidden = true
createNewAccountButton.isHidden = true
} else {
actionButton.title = NSLocalizedString("Create", comment: "Create")
signInTextField.stringValue = NSLocalizedString("Sign in to your NewsBlur account.", comment: "SignIn")
}
}
@@ -113,4 +121,9 @@ class AccountsNewsBlurWindowController: NSWindowController {
}
}
}
@IBAction func createAccountWithProvider(_ sender: Any) {
NSWorkspace.shared.open(URL(string: "https://newsblur.com")!)
}
}

View File

@@ -45,10 +45,7 @@ final class AccountsPreferencesViewController: NSViewController {
rTable.size.width = tableView.superview!.frame.size.width
tableView.frame = rTable
// Set initial row selection
if sortedAccounts.count > 0 {
tableView.selectRow(0)
}
hideController()
}
@IBAction func addAccount(_ sender: Any) {
@@ -226,12 +223,11 @@ private extension AccountsPreferencesViewController {
func showController(_ controller: NSViewController) {
hideController()
addChild(controller)
controller.view.translatesAutoresizingMaskIntoConstraints = false
detailView.addSubview(controller.view)
detailView.addFullSizeConstraints(forSubview: controller.view)
}
func hideController() {
@@ -239,6 +235,27 @@ private extension AccountsPreferencesViewController {
children.removeAll()
controller.view.removeFromSuperview()
}
if tableView.selectedRow == -1 {
var helpText = ""
if sortedAccounts.count == 0 {
helpText = NSLocalizedString("Add an account by clicking the + button.", comment: "Add Account Explainer")
} else {
helpText = NSLocalizedString("Select an account or add a new account by clicking the + button.", comment: "Add Account Explainer")
}
let textHostingController = NSHostingController(rootView:
AddAccountHelpView(delegate: addAccountDelegate, helpText: helpText))
addChild(textHostingController)
textHostingController.view.translatesAutoresizingMaskIntoConstraints = false
detailView.addSubview(textHostingController.view)
detailView.addConstraints([
NSLayoutConstraint(item: textHostingController.view, attribute: .top, relatedBy: .equal, toItem: detailView, attribute: .top, multiplier: 1, constant: 1),
NSLayoutConstraint(item: textHostingController.view, attribute: .bottom, relatedBy: .equal, toItem: detailView, attribute: .bottom, multiplier: 1, constant: -deleteButton.frame.height),
NSLayoutConstraint(item: textHostingController.view, attribute: .width, relatedBy: .equal, toItem: detailView, attribute: .width, multiplier: 1, constant: 1)
])
}
}
}

View File

@@ -3,6 +3,7 @@
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
<capability name="Named colors" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@@ -11,8 +12,9 @@
<outlet property="actionButton" destination="9mz-D9-krh" id="ozu-6Q-9Lb"/>
<outlet property="apiURLLabel" destination="H6f-t4-SMg" id="1ac-qe-jw2"/>
<outlet property="apiURLTextField" destination="d7d-ZV-CcZ" id="Af4-uM-Dgd"/>
<outlet property="errorMessageLabel" destination="byK-Sd-r7F" id="8zt-9d-dWQ"/>
<outlet property="errorMessageLabel" destination="Gyz-Mz-v08" id="fwM-K9-xnR"/>
<outlet property="gridView" destination="zBB-JH-huI" id="ghz-ki-b0V"/>
<outlet property="noAccountTextField" destination="R8e-67-Bwh" id="7oG-ro-jcB"/>
<outlet property="passwordTextField" destination="JSa-LY-zNQ" id="E9W-0F-69m"/>
<outlet property="progressIndicator" destination="B0W-bh-Evv" id="Tiq-gx-s3F"/>
<outlet property="titleImageView" destination="Ssh-Dh-xbg" id="8Iy-jf-EYR"/>
@@ -28,49 +30,19 @@
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="196" y="240" width="433" height="249"/>
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
<view key="contentView" misplaced="YES" id="se5-gp-TjO">
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="433" height="249"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" spacing="19" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7Ht-Fn-0Ya">
<rect key="frame" x="113" y="219" width="208" height="38"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="0.0" y="0.0" width="36" height="36"/>
<constraints>
<constraint firstAttribute="height" constant="36" id="Ern-Kk-8LX"/>
<constraint firstAttribute="width" constant="36" id="PLS-68-NMc"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="53" y="0.0" width="157" height="38"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Reader API" id="ras-dj-nP8">
<font key="font" metaFont="system" size="32"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
<rect key="frame" x="217" y="229" width="0.0" height="0.0"/>
</stackView>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="12" columnSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="82" y="60" width="270" height="127"/>
<constraints>
<constraint firstItem="byK-Sd-r7F" firstAttribute="width" secondItem="d7d-ZV-CcZ" secondAttribute="width" id="PDt-cC-RvD"/>
</constraints>
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="7" columnSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
<rect key="frame" x="80" y="99" width="270" height="77"/>
<rows>
<gridRow id="DRl-lC-vUc"/>
<gridRow id="eW8-uH-txq"/>
<gridRow id="ebD-On-mOK"/>
<gridRow id="DbI-7g-Xme"/>
</rows>
<columns>
<gridColumn id="fCQ-jY-Mts"/>
@@ -79,7 +51,7 @@
<gridCells>
<gridCell row="DRl-lC-vUc" column="fCQ-jY-Mts" id="4DI-01-jGD">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Zy6-9c-8TI">
<rect key="frame" x="23" y="109" width="41" height="16"/>
<rect key="frame" x="23" y="59" width="41" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Login:" id="DqN-SV-v35">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -89,11 +61,11 @@
</gridCell>
<gridCell row="DRl-lC-vUc" column="7CY-bX-6x4" id="Z0b-qS-MUJ">
<textField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="78p-Cf-f55">
<rect key="frame" x="70" y="106" width="200" height="21"/>
<rect key="frame" x="70" y="56" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="Qin-jm-4zt"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="fCk-Tf-q01">
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="username" drawsBackground="YES" id="fCk-Tf-q01">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -102,7 +74,7 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="fCQ-jY-Mts" id="Hqa-3w-cQv">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wEx-TM-rPM">
<rect key="frame" x="-2" y="76" width="66" height="16"/>
<rect key="frame" x="-2" y="31" width="66" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Password:" id="7g8-Kk-ISg">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -112,11 +84,11 @@
</gridCell>
<gridCell row="eW8-uH-txq" column="7CY-bX-6x4" id="m16-3v-9pf">
<secureTextField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JSa-LY-zNQ">
<rect key="frame" x="70" y="73" width="200" height="21"/>
<rect key="frame" x="70" y="28" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="eal-wa-1nU"/>
</constraints>
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<secureTextFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="••••••••" drawsBackground="YES" usesSingleLineMode="YES" id="trK-OG-tBe">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -128,7 +100,7 @@
</gridCell>
<gridCell row="ebD-On-mOK" column="fCQ-jY-Mts" id="xwR-xz-N6h">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="H6f-t4-SMg">
<rect key="frame" x="7" y="43" width="57" height="16"/>
<rect key="frame" x="7" y="3" width="57" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="API URL:" id="zBm-dZ-EF1">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
@@ -138,7 +110,7 @@
</gridCell>
<gridCell row="ebD-On-mOK" column="7CY-bX-6x4" id="Wd5-Zp-t61">
<textField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="d7d-ZV-CcZ" userLabel="API URL Text Field">
<rect key="frame" x="70" y="40" width="200" height="21"/>
<rect key="frame" x="70" y="0.0" width="200" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="Mki-bb-tDu"/>
</constraints>
@@ -149,17 +121,6 @@
</textFieldCell>
</textField>
</gridCell>
<gridCell row="DbI-7g-Xme" column="fCQ-jY-Mts" xPlacement="leading" id="xX0-vn-AId"/>
<gridCell row="DbI-7g-Xme" column="7CY-bX-6x4" yPlacement="top" id="hk5-St-E4y">
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="byK-Sd-r7F">
<rect key="frame" x="68" y="12" width="204" height="16"/>
<textFieldCell key="cell" id="0yh-Ab-UTX">
<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>
</gridCell>
</gridCells>
</gridView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9mz-D9-krh">
@@ -189,21 +150,100 @@ Gw
</connections>
</button>
<progressIndicator hidden="YES" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="B0W-bh-Evv">
<rect key="frame" x="209" y="195" width="16" height="16"/>
<rect key="frame" x="256" y="20" width="16" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="16" id="QuJ-Kk-QvB"/>
<constraint firstAttribute="height" constant="16" id="SbP-MC-kng"/>
</constraints>
</progressIndicator>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Ssh-Dh-xbg">
<rect key="frame" x="20" y="179" width="50" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="NYV-hM-AYz"/>
<constraint firstAttribute="width" constant="50" id="qWk-4n-LXb"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="78" y="213" width="337" height="16"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Sign in to a Reader API account." id="ras-dj-nP8">
<font key="font" textStyle="headline" name=".SFNS-Bold"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="R8e-67-Bwh">
<rect key="frame" x="78" y="193" width="174" height="15"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Don't have a Reader account?" id="ker-hu-FEc">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kUQ-aj-Iu6">
<rect key="frame" x="251" y="194" width="99" height="13"/>
<constraints>
<constraint firstAttribute="height" constant="13" id="NOr-5b-kRd"/>
</constraints>
<buttonCell key="cell" type="roundRect" title="Create one here." bezelStyle="roundedRect" alignment="center" state="on" imageScaling="proportionallyDown" inset="2" id="xY9-Rw-8kx">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="cellTitle"/>
</buttonCell>
<color key="contentTintColor" name="AccentColor"/>
<connections>
<action selector="createAccountWithProvider:" target="-2" id="eQi-od-QJ3"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="q5z-LG-0Fm">
<rect key="frame" x="78" y="45" width="337" height="39"/>
<constraints>
<constraint firstAttribute="height" constant="39" id="Zv4-mj-po0"/>
<constraint firstAttribute="width" constant="333" id="xsi-I6-XNM"/>
</constraints>
<textFieldCell key="cell" title="Your username and password will be encrypted and stored in Keychain. " id="Ec9-bB-tQz">
<font key="font" textStyle="callout" name=".SFNS-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Gyz-Mz-v08">
<rect key="frame" x="18" y="23" width="230" height="14"/>
<constraints>
<constraint firstAttribute="width" constant="226" id="0Fh-f0-A3K"/>
</constraints>
<textFieldCell key="cell" id="AGV-EV-VmY">
<font key="font" textStyle="subheadline" name=".SFNS-Regular"/>
<color key="textColor" name="systemRedColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="0Fq-9s-pKG"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="R8e-67-Bwh" secondAttribute="bottom" constant="17" id="2Ve-1M-0xg"/>
<constraint firstItem="kUQ-aj-Iu6" firstAttribute="centerY" secondItem="R8e-67-Bwh" secondAttribute="centerY" id="2hZ-w7-Lne"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="5a7-jI-SC9"/>
<constraint firstAttribute="bottom" secondItem="B0W-bh-Evv" secondAttribute="bottom" constant="20" symbolic="YES" id="7OS-Ie-XXN"/>
<constraint firstItem="XAM-Hb-0Hw" firstAttribute="leading" secondItem="B0W-bh-Evv" secondAttribute="trailing" constant="8" symbolic="YES" id="Ame-HX-mdC"/>
<constraint firstItem="q5z-LG-0Fm" firstAttribute="leading" secondItem="R8e-67-Bwh" secondAttribute="leading" id="B2k-oM-YfG"/>
<constraint firstItem="kUQ-aj-Iu6" firstAttribute="leading" secondItem="R8e-67-Bwh" secondAttribute="trailing" constant="1" id="BWQ-ly-Hcg"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="leading" secondItem="XAM-Hb-0Hw" secondAttribute="trailing" constant="12" symbolic="YES" id="CC8-HR-FDy"/>
<constraint firstAttribute="trailing" secondItem="lti-yM-8LV" secondAttribute="trailing" constant="20" symbolic="YES" id="CeA-ET-oeg"/>
<constraint firstItem="Ssh-Dh-xbg" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="EKF-Yq-DxZ"/>
<constraint firstItem="q5z-LG-0Fm" firstAttribute="top" secondItem="zBB-JH-huI" secondAttribute="bottom" constant="15" id="Hdb-dd-lM9"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="leading" secondItem="Gyz-Mz-v08" secondAttribute="trailing" constant="10" id="IUE-sL-VHF"/>
<constraint firstItem="XAM-Hb-0Hw" firstAttribute="centerY" secondItem="9mz-D9-krh" secondAttribute="centerY" id="M2M-fb-kfR"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="leading" secondItem="R8e-67-Bwh" secondAttribute="leading" id="Mqa-R7-znW"/>
<constraint firstAttribute="bottom" secondItem="9mz-D9-krh" secondAttribute="bottom" constant="20" id="PK2-Ye-400"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="top" secondItem="B0W-bh-Evv" secondAttribute="bottom" constant="8" id="V7z-a7-OOG"/>
<constraint firstItem="9mz-D9-krh" firstAttribute="top" secondItem="zBB-JH-huI" secondAttribute="bottom" constant="20" symbolic="YES" id="Wu3-hp-Vzh"/>
<constraint firstItem="zBB-JH-huI" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="aFI-4s-mMv"/>
<constraint firstItem="R8e-67-Bwh" firstAttribute="leading" secondItem="Ssh-Dh-xbg" secondAttribute="trailing" constant="10" id="ah8-Mj-rXV"/>
<constraint firstItem="Gyz-Mz-v08" firstAttribute="centerY" secondItem="XAM-Hb-0Hw" secondAttribute="centerY" id="cSM-1G-AUG"/>
<constraint firstAttribute="trailing" secondItem="9mz-D9-krh" secondAttribute="trailing" constant="20" id="fVQ-zN-rKd"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="8" id="gq2-tB-pXH"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" id="jlY-Jg-KJR"/>
<constraint firstItem="B0W-bh-Evv" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="lrN-Gd-iXd"/>
<constraint firstItem="Gyz-Mz-v08" firstAttribute="leading" secondItem="se5-gp-TjO" secondAttribute="leading" constant="20" symbolic="YES" id="p26-D7-Oi8"/>
<constraint firstAttribute="trailing" secondItem="q5z-LG-0Fm" secondAttribute="trailing" constant="20" id="rky-el-9AK"/>
<constraint firstItem="lti-yM-8LV" firstAttribute="top" secondItem="se5-gp-TjO" secondAttribute="top" constant="20" symbolic="YES" id="t4L-Nb-T2M"/>
<constraint firstItem="7Ht-Fn-0Ya" firstAttribute="centerX" secondItem="se5-gp-TjO" secondAttribute="centerX" id="tAZ-Te-w3H"/>
<constraint firstItem="R8e-67-Bwh" firstAttribute="top" secondItem="lti-yM-8LV" secondAttribute="bottom" constant="5" id="vL5-xO-f7d"/>
</constraints>
</view>
<connections>
@@ -212,4 +252,9 @@ Gw
<point key="canvasLocation" x="116.5" y="136.5"/>
</window>
</objects>
<resources>
<namedColor name="AccentColor">
<color red="0.030999999493360519" green="0.41600000858306885" blue="0.93300002813339233" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</namedColor>
</resources>
</document>

View File

@@ -23,6 +23,7 @@ class AccountsReaderAPIWindowController: NSWindowController {
@IBOutlet weak var passwordTextField: NSSecureTextField!
@IBOutlet weak var errorMessageLabel: NSTextField!
@IBOutlet weak var actionButton: NSButton!
@IBOutlet weak var noAccountTextField: NSTextField!
var account: Account?
var accountType: AccountType?
@@ -38,19 +39,23 @@ class AccountsReaderAPIWindowController: NSWindowController {
switch accountType {
case .freshRSS:
titleImageView.image = AppAssets.accountFreshRSS
titleLabel.stringValue = NSLocalizedString("FreshRSS", comment: "FreshRSS")
titleLabel.stringValue = NSLocalizedString("Sign in to your FreshRSS account.", comment: "FreshRSS")
noAccountTextField.stringValue = NSLocalizedString("Don't have a FreshRSS account?", comment: "No FreshRSS")
case .inoreader:
titleImageView.image = AppAssets.accountInoreader
titleLabel.stringValue = NSLocalizedString("InoReader", comment: "InoReader")
titleLabel.stringValue = NSLocalizedString("Sign in to your InoReader account.", comment: "InoReader")
gridView.row(at: 2).isHidden = true
noAccountTextField.stringValue = NSLocalizedString("Don't have an InoReader account?", comment: "No InoReader")
case .bazQux:
titleImageView.image = AppAssets.accountBazQux
titleLabel.stringValue = NSLocalizedString("BazQux", comment: "BazQux")
titleLabel.stringValue = NSLocalizedString("Sign in to your BazQux account.", comment: "BazQux")
gridView.row(at: 2).isHidden = true
noAccountTextField.stringValue = NSLocalizedString("Don't have a BazQux account?", comment: "No BazQux")
case .theOldReader:
titleImageView.image = AppAssets.accountTheOldReader
titleLabel.stringValue = NSLocalizedString("The Old Reader", comment: "The Old Reader")
titleLabel.stringValue = NSLocalizedString("Sign in to your The Old Reader account.", comment: "The Old Reader")
gridView.row(at: 2).isHidden = true
noAccountTextField.stringValue = NSLocalizedString("Don't have a The Old Reader account?", comment: "No OldReader")
default:
break
}
@@ -172,5 +177,21 @@ class AccountsReaderAPIWindowController: NSWindowController {
}
}
@IBAction func createAccountWithProvider(_ sender: Any) {
switch accountType {
case .freshRSS:
NSWorkspace.shared.open(URL(string: "https://freshrss.org")!)
case .inoreader:
NSWorkspace.shared.open(URL(string: "https://www.inoreader.com")!)
case .bazQux:
NSWorkspace.shared.open(URL(string: "https://bazqux.com")!)
case .theOldReader:
NSWorkspace.shared.open(URL(string: "https://theoldreader.com")!)
default:
return
}
}
}

View File

@@ -0,0 +1,60 @@
//
// AddAccountHelpView.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 4/11/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
struct AddAccountHelpView: View {
let accountTypes: [AccountType] = AddAccountSections.allOrdered.sectionContent
var delegate: AccountsPreferencesAddAccountDelegate?
var helpText: String
@State private var hoveringId: String? = nil
@State private var iCloudUnavailableError: Bool = false
var body: some View {
VStack {
HStack {
ForEach(accountTypes, id: \.self) { account in
account.image()
.resizable()
.frame(width: 20, height: 20, alignment: .center)
.onTapGesture {
if account == .cloudKit && AccountManager.shared.accounts.contains(where: { $0.type == .cloudKit }) {
iCloudUnavailableError = true
} else {
delegate?.presentSheetForAccount(account)
}
hoveringId = nil
}
.onHover(perform: { hovering in
if hovering {
hoveringId = account.localizedAccountName()
} else {
hoveringId = nil
}
})
.scaleEffect(hoveringId == account.localizedAccountName() ? 1.2 : 1)
.shadow(radius: hoveringId == account.localizedAccountName() ? 0.8 : 0)
}
}
Text(helpText)
.multilineTextAlignment(.center)
.padding(.top, 8)
}
.alert(isPresented: $iCloudUnavailableError, content: {
Alert(title: Text(NSLocalizedString("Error", comment: "Error")),
message: Text(NSLocalizedString("You've already set up an iCloud account.", comment: "Error")),
dismissButton: Alert.Button.cancel({
iCloudUnavailableError = false
}))
})
}
}

View File

@@ -9,11 +9,12 @@
import SwiftUI
import Account
private enum AddAccountSections: Int, CaseIterable {
enum AddAccountSections: Int, CaseIterable {
case local = 0
case icloud
case web
case selfhosted
case allOrdered
var sectionHeader: String {
switch self {
@@ -25,6 +26,8 @@ private enum AddAccountSections: Int, CaseIterable {
return NSLocalizedString("Web", comment: "Web Account")
case .selfhosted:
return NSLocalizedString("Self-hosted", comment: "Self hosted Account")
case .allOrdered:
return ""
}
}
@@ -38,6 +41,8 @@ private enum AddAccountSections: Int, CaseIterable {
return NSLocalizedString("Web accounts sync your subscriptions across all your devices.", comment: "Web Account")
case .selfhosted:
return NSLocalizedString("Self-hosted accounts sync your subscriptions across all your devices.", comment: "Self hosted Account")
case .allOrdered:
return ""
}
}
@@ -51,6 +56,11 @@ private enum AddAccountSections: Int, CaseIterable {
return [.bazQux, .feedbin, .feedly, .feedWrangler, .inoreader, .newsBlur, .theOldReader]
case .selfhosted:
return [.freshRSS]
case .allOrdered:
return AddAccountSections.local.sectionContent +
AddAccountSections.icloud.sectionContent +
AddAccountSections.web.sectionContent +
AddAccountSections.selfhosted.sectionContent
}
}
}
@@ -141,8 +151,6 @@ struct AddAccountsView: View {
.aspectRatio(contentMode: .fit)
.frame(width: 25, height: 25, alignment: .center)
.padding(.leading, 4)
Text(account.localizedAccountName())
}
.tag(account)

View File

@@ -0,0 +1,61 @@
//
// EnableExtensionPointHelpView.swift
// NetNewsWire
//
// Created by Stuart Breckenridge on 4/11/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import AppKit
import SwiftUI
import RSCore
struct EnableExtensionPointHelpView: View {
var extensionPoints: [ExtensionPoint.Type] {
let types = ExtensionPointManager.shared.availableExtensionPointTypes.filter({ $0 is SendToCommand.Type }) +
ExtensionPointManager.shared.availableExtensionPointTypes.filter({ !($0 is SendToCommand.Type) })
return types
}
var helpText: String
weak var preferencesController: ExtensionPointPreferencesViewController?
@State private var hoveringId: String?
var body: some View {
VStack {
HStack {
ForEach(0..<extensionPoints.count, content: { i in
Image(nsImage: extensionPoints[i].image)
.resizable()
.frame(width: 20, height: 20, alignment: .center)
.onTapGesture {
preferencesController?.enableExtensionPointFromSelection(extensionPoints[i])
hoveringId = nil
}
.onHover(perform: { hovering in
if hovering {
hoveringId = extensionPoints[i].title
} else {
hoveringId = nil
}
})
.scaleEffect(hoveringId == extensionPoints[i].title ? 1.2 : 1)
.shadow(radius: hoveringId == extensionPoints[i].title ? 0.8 : 0)
})
if ExtensionPointManager.shared.availableExtensionPointTypes.count == 0 {
Image("markUnread")
.resizable()
.renderingMode(.template)
.frame(width: 30, height: 30, alignment: .center)
.foregroundColor(.green)
}
}
Text(helpText)
.multilineTextAlignment(.center)
.padding(.top, 8)
}
}
}

View File

@@ -10,14 +10,17 @@ import AppKit
import SwiftUI
import RSCore
struct EnableExtensionPointView: View {
weak var parent: NSHostingController<EnableExtensionPointView>? // required because presentationMode.dismiss() doesn't work
weak var enabler: ExtensionPointPreferencesEnabler?
@State private var extensionPointTypeName = String(describing: Self.feedProviderExtensionPointTypes.first!)
@State private var extensionPointTypeName = String(describing: Self.sendToCommandExtensionPointTypes.first)
private var selectedType: ExtensionPoint.Type?
init(enabler: ExtensionPointPreferencesEnabler?) {
init(enabler: ExtensionPointPreferencesEnabler?, selectedType: ExtensionPoint.Type? ) {
self.enabler = enabler
self.selectedType = selectedType
}
var body: some View {
@@ -48,7 +51,7 @@ struct EnableExtensionPointView: View {
Text("Cancel")
.frame(width: 80)
})
.accessibility(label: Text("Add Account"))
.accessibility(label: Text("Add Extension"))
}
if #available(OSX 11.0, *) {
Button(action: {
@@ -58,9 +61,9 @@ struct EnableExtensionPointView: View {
Text("Continue")
.frame(width: 80)
})
.help("Add Account")
.help("Add Extension")
.keyboardShortcut(.defaultAction)
.disabled(disableContinue())
} else {
Button(action: {
enabler?.enable(typeFromName(extensionPointTypeName))
@@ -69,6 +72,7 @@ struct EnableExtensionPointView: View {
Text("Continue")
.frame(width: 80)
})
.disabled(disableContinue())
}
}
.padding(.top, 12)
@@ -78,6 +82,11 @@ struct EnableExtensionPointView: View {
.fixedSize(horizontal: false, vertical: true)
.frame(width: 420)
.padding()
.onAppear {
if selectedType != nil {
extensionPointTypeName = String(describing: selectedType!)
}
}
}
var feedProviderExtensionPoints: some View {
@@ -101,13 +110,13 @@ struct EnableExtensionPointView: View {
Text(extensionPointType.title)
}
.tag(extensionPointTypeNames)
.tag(extensionPointTypeName)
})
})
.pickerStyle(RadioGroupPickerStyle())
.offset(x: 7.5, y: 0)
Text("An extension point that makes websites appear to provide RSS feeds for their content.")
Text("An extension that makes websites appear to provide RSS feeds for their content.")
.foregroundColor(.gray)
.font(.caption)
.padding(.horizontal)
@@ -138,13 +147,13 @@ struct EnableExtensionPointView: View {
Text(extensionPointType.title)
}
.tag(extensionPointTypeNames)
.tag(extensionPointTypeName)
})
})
.pickerStyle(RadioGroupPickerStyle())
.offset(x: 7.5, y: 0)
Text("An extension point that enables a share menu item that passes article data to a third-party application.")
Text("An extension that enables a share menu item that passes article data to a third-party application.")
.foregroundColor(.gray)
.font(.caption)
.padding(.horizontal)
@@ -169,4 +178,11 @@ struct EnableExtensionPointView: View {
}
fatalError()
}
func disableContinue() -> Bool {
ExtensionPointManager.shared.availableExtensionPointTypes.count == 0
}
}

View File

@@ -41,14 +41,17 @@ final class ExtensionPointPreferencesViewController: NSViewController {
showDefaultView()
// Set initial row selection
if activeExtensionPoints.count > 0 {
tableView.selectRow(0)
}
}
@IBAction func enableExtensionPoints(_ sender: Any) {
let controller = NSHostingController(rootView: EnableExtensionPointView(enabler: self))
let controller = NSHostingController(rootView: EnableExtensionPointView(enabler: self, selectedType: nil))
controller.rootView.parent = controller
presentAsSheet(controller)
}
func enableExtensionPointFromSelection(_ selection: ExtensionPoint.Type) {
let controller = NSHostingController(rootView: EnableExtensionPointView(enabler: self, selectedType: selection))
controller.rootView.parent = controller
presentAsSheet(controller)
}
@@ -179,6 +182,33 @@ private extension ExtensionPointPreferencesViewController {
func showDefaultView() {
activeExtensionPoints = Array(ExtensionPointManager.shared.activeExtensionPoints.values).sorted(by: { $0.title < $1.title })
tableView.reloadData()
if tableView.selectedRow == -1 {
var helpText = ""
if ExtensionPointManager.shared.availableExtensionPointTypes.count == 0 {
helpText = NSLocalizedString("You've added all available extension points.", comment: "Extension Explainer")
}
else if activeExtensionPoints.count == 0 {
helpText = NSLocalizedString("Add an extension by clicking the + button.", comment: "Extension Explainer")
} else {
helpText = NSLocalizedString("Select an extension or add a new extension point by clicking the + button.", comment: "Extension Explainer")
}
if let controller = children.first {
children.removeAll()
controller.view.removeFromSuperview()
}
let textHostingController = NSHostingController(rootView: EnableExtensionPointHelpView(helpText: helpText, preferencesController: self))
addChild(textHostingController)
textHostingController.view.translatesAutoresizingMaskIntoConstraints = false
detailView.addSubview(textHostingController.view)
detailView.addConstraints([
NSLayoutConstraint(item: textHostingController.view, attribute: .top, relatedBy: .equal, toItem: detailView, attribute: .top, multiplier: 1, constant: 1),
NSLayoutConstraint(item: textHostingController.view, attribute: .bottom, relatedBy: .equal, toItem: detailView, attribute: .bottom, multiplier: 1, constant: -deleteButton.frame.height),
NSLayoutConstraint(item: textHostingController.view, attribute: .width, relatedBy: .equal, toItem: detailView, attribute: .width, multiplier: 1, constant: 1)
])
}
}
func showController(_ controller: NSViewController) {
@@ -195,6 +225,28 @@ private extension ExtensionPointPreferencesViewController {
children.removeAll()
controller.view.removeFromSuperview()
}
if tableView.selectedRow == -1 {
var helpText = ""
if ExtensionPointManager.shared.availableExtensionPointTypes.count == 0 {
helpText = NSLocalizedString("You've added all available extension points.", comment: "Extension Explainer")
}
else if activeExtensionPoints.count == 0 {
helpText = NSLocalizedString("Add an extension by clicking the + button.", comment: "Extension Explainer")
} else {
helpText = NSLocalizedString("Select an extension or add a new extension point by clicking the + button.", comment: "Extension Explainer")
}
let textHostingController = NSHostingController(rootView: EnableExtensionPointHelpView(helpText: helpText, preferencesController: self))
addChild(textHostingController)
textHostingController.view.translatesAutoresizingMaskIntoConstraints = false
detailView.addSubview(textHostingController.view)
detailView.addConstraints([
NSLayoutConstraint(item: textHostingController.view, attribute: .top, relatedBy: .equal, toItem: detailView, attribute: .top, multiplier: 1, constant: 1),
NSLayoutConstraint(item: textHostingController.view, attribute: .bottom, relatedBy: .equal, toItem: detailView, attribute: .bottom, multiplier: 1, constant: -deleteButton.frame.height),
NSLayoutConstraint(item: textHostingController.view, attribute: .width, relatedBy: .equal, toItem: detailView, attribute: .width, multiplier: 1, constant: 1)
])
}
}
func enableOauth1(_ provider: OAuth1SwiftProvider.Type, extensionPointType: ExtensionPoint.Type) {
@@ -254,3 +306,5 @@ private extension ExtensionPointPreferencesViewController {
}
}

View File

@@ -15,10 +15,8 @@ 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?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
@@ -44,17 +42,6 @@ final class GeneralPreferencesViewController: NSViewController {
// MARK: - Actions
@IBAction func rssReaderPopupDidChangeValue(_ sender: Any?) {
guard let menuItem = defaultRSSReaderPopup.selectedItem else {
return
}
guard let bundleID = menuItem.representedObject as? String else {
return
}
registerAppWithBundleID(bundleID)
updateUI()
}
@IBAction func browserPopUpDidChangeValue(_ sender: Any?) {
guard let menuItem = defaultBrowserPopup.selectedItem else {
return
@@ -115,69 +102,10 @@ private extension GeneralPreferencesViewController {
}
func updateUI() {
rssReaderInfo = RSSReaderInfo()
updateBrowserPopup()
updateRSSReaderPopup()
updateHideUnreadCountCheckbox()
}
func updateRSSReaderPopup() {
// Top item should always be: NetNewsWire (this app)
// Additional items should be sorted alphabetically.
// Any older versions of NetNewsWire should be listed as: NetNewsWire (old version)
let menu = NSMenu(title: "RSS Readers")
let netNewsWireBundleID = Bundle.main.bundleIdentifier!
let thisAppParentheticalComment = NSLocalizedString("(this app)", comment: "Preferences default RSS Reader popup")
let thisAppName = "NetNewsWire \(thisAppParentheticalComment)"
let netNewsWireMenuItem = NSMenuItem(title: thisAppName, action: nil, keyEquivalent: "")
netNewsWireMenuItem.representedObject = netNewsWireBundleID
menu.addItem(netNewsWireMenuItem)
let readersToList = rssReaderInfo.rssReaders.filter { $0.bundleID != netNewsWireBundleID }
let sortedReaders = readersToList.sorted { (reader1, reader2) -> Bool in
return reader1.nameMinusAppSuffix.localizedStandardCompare(reader2.nameMinusAppSuffix) == .orderedAscending
}
let oldVersionParentheticalComment = NSLocalizedString("(old version)", comment: "Preferences default RSS Reader popup")
for rssReader in sortedReaders {
var appName = rssReader.nameMinusAppSuffix
if appName.contains("NetNewsWire") {
appName = "\(appName) \(oldVersionParentheticalComment)"
}
let menuItem = NSMenuItem(title: appName, action: nil, keyEquivalent: "")
menuItem.representedObject = rssReader.bundleID
menu.addItem(menuItem)
}
defaultRSSReaderPopup.menu = menu
func insertAndSelectNoneMenuItem() {
let noneTitle = NSLocalizedString("None", comment: "Preferences default RSS Reader popup")
let menuItem = NSMenuItem(title: noneTitle, action: nil, keyEquivalent: "")
defaultRSSReaderPopup.menu!.insertItem(menuItem, at: 0)
defaultRSSReaderPopup.selectItem(at: 0)
}
guard let defaultRSSReaderBundleID = rssReaderInfo.defaultRSSReaderBundleID else {
insertAndSelectNoneMenuItem()
return
}
for menuItem in defaultRSSReaderPopup.menu!.items {
guard let bundleID = menuItem.representedObject as? String else {
continue
}
if bundleID == defaultRSSReaderBundleID {
defaultRSSReaderPopup.select(menuItem)
return
}
}
insertAndSelectNoneMenuItem()
}
func registerAppWithBundleID(_ bundleID: String) {
NSWorkspace.shared.setDefaultAppBundleID(forURLScheme: "feed", to: bundleID)
NSWorkspace.shared.setDefaultAppBundleID(forURLScheme: "feeds", to: bundleID)
@@ -245,74 +173,3 @@ private extension GeneralPreferencesViewController {
}
}
// MARK: - RSSReaderInfo
private struct RSSReaderInfo {
let defaultRSSReaderBundleID: String?
let rssReaders: Set<RSSReader>
static let feedURLScheme = "feed:"
init() {
let defaultRSSReaderBundleID = NSWorkspace.shared.defaultAppBundleID(forURLScheme: RSSReaderInfo.feedURLScheme)
self.defaultRSSReaderBundleID = defaultRSSReaderBundleID
self.rssReaders = RSSReaderInfo.fetchRSSReaders(defaultRSSReaderBundleID)
}
static func fetchRSSReaders(_ defaultRSSReaderBundleID: String?) -> Set<RSSReader> {
let rssReaderBundleIDs = NSWorkspace.shared.bundleIDsForApps(forURLScheme: feedURLScheme)
var rssReaders = Set<RSSReader>()
if let defaultRSSReaderBundleID = defaultRSSReaderBundleID, let defaultReader = RSSReader(bundleID: defaultRSSReaderBundleID) {
rssReaders.insert(defaultReader)
}
rssReaderBundleIDs.forEach { (bundleID) in
if let reader = RSSReader(bundleID: bundleID) {
rssReaders.insert(reader)
}
}
return rssReaders
}
}
// MARK: - RSSReader
private struct RSSReader: Hashable {
let bundleID: String
let name: String
let nameMinusAppSuffix: String
let path: String
init?(bundleID: String) {
guard let path = NSWorkspace.shared.absolutePathForApplication(withBundleIdentifier: bundleID) else {
return nil
}
self.path = path
self.bundleID = bundleID
let name = (path as NSString).lastPathComponent
self.name = name
if name.hasSuffix(".app") {
self.nameMinusAppSuffix = name.stripping(suffix: ".app")
}
else {
self.nameMinusAppSuffix = name
}
}
// MARK: - Hashable
func hash(into hasher: inout Hasher) {
hasher.combine(bundleID)
}
// MARK: - Equatable
static func ==(lhs: RSSReader, rhs: RSSReader) -> Bool {
return lhs.bundleID == rhs.bundleID
}
}

View File

@@ -1,12 +1,12 @@
{
"images" : [
{
"filename" : "shareExtensionx1.png",
"filename" : "Icon-MacOS-32x32@1x.png.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "shareExtensionx2.png",
"filename" : "Icon-MacOS-32x32@2x.png.png",
"idiom" : "universal",
"scale" : "2x"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -72,6 +72,7 @@ final class AppDefaults: ObservableObject {
static let confirmMarkAllAsRead = "confirmMarkAllAsRead"
// macOS Defaults
static let articleTextSize = "articleTextSize"
static let openInBrowserInBackground = "openInBrowserInBackground"
static let defaultBrowserID = "defaultBrowserID"
static let checkForUpdatesAutomatically = "checkForUpdatesAutomatically"
@@ -231,6 +232,16 @@ final class AppDefaults: ObservableObject {
@AppStorage(wrappedValue: false, Key.articleFullscreenEnabled, store: store) var articleFullscreenEnabled: Bool
@AppStorage(wrappedValue: 3, Key.articleTextSize, store: store) var articleTextSizeTag: Int {
didSet {
objectWillChange.send()
}
}
var articleTextSize: ArticleTextSize {
ArticleTextSize(rawValue: articleTextSizeTag) ?? ArticleTextSize.large
}
// MARK: Refresh
var lastRefresh: Date? {
set {

View File

@@ -242,7 +242,7 @@ private extension SidebarModel {
/// - Parameter articles: An array of `Article`s.
/// - Warning: An `UndoManager` is created here as the `Environment`'s undo manager appears to be `nil`.
func markAllAsRead(_ articles: [Article]) {
guard let undoManager = undoManager ?? UndoManager(),
guard let undoManager = undoManager,
let markAsReadCommand = MarkStatusCommand(initialArticles: articles, markingRead: true, undoManager: undoManager) else {
return
}

View File

@@ -20,7 +20,8 @@ import RSCore
}
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, sharingServicesForItems items: [Any], proposedSharingServices proposedServices: [NSSharingService]) -> [NSSharingService] {
return proposedServices + SharingServicePickerDelegate.customSharingServices(for: items)
let filteredServices = proposedServices.filter { $0.menuItemTitle != "NetNewsWire" }
return filteredServices + SharingServicePickerDelegate.customSharingServices(for: items)
}
func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, delegateFor sharingService: NSSharingService) -> NSSharingServiceDelegate? {

View File

@@ -9,6 +9,10 @@
/* Begin PBXBuildFile section */
1704053424E5985A00A00787 /* SceneNavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1704053324E5985A00A00787 /* SceneNavigationModel.swift */; };
1704053524E5985A00A00787 /* SceneNavigationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1704053324E5985A00A00787 /* SceneNavigationModel.swift */; };
1710B9132552354E00679C0D /* AddAccountHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1710B9122552354E00679C0D /* AddAccountHelpView.swift */; };
1710B9142552354E00679C0D /* AddAccountHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1710B9122552354E00679C0D /* AddAccountHelpView.swift */; };
1710B929255246F900679C0D /* EnableExtensionPointHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1710B928255246F900679C0D /* EnableExtensionPointHelpView.swift */; };
1710B92A255246F900679C0D /* EnableExtensionPointHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1710B928255246F900679C0D /* EnableExtensionPointHelpView.swift */; };
1717535624BADF33004498C6 /* GeneralPreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1717535524BADF33004498C6 /* GeneralPreferencesModel.swift */; };
171BCB8C24CB08A3006E22D9 /* FixAccountCredentialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BCB8B24CB08A3006E22D9 /* FixAccountCredentialView.swift */; };
171BCB8D24CB08A3006E22D9 /* FixAccountCredentialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BCB8B24CB08A3006E22D9 /* FixAccountCredentialView.swift */; };
@@ -495,6 +499,12 @@
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */; };
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; };
51DC07982552083500A3F79F /* ArticleTextSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC07972552083500A3F79F /* ArticleTextSize.swift */; };
51DC07992552083500A3F79F /* ArticleTextSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC07972552083500A3F79F /* ArticleTextSize.swift */; };
51DC079A2552083500A3F79F /* ArticleTextSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC07972552083500A3F79F /* ArticleTextSize.swift */; };
51DC079B2552083500A3F79F /* ArticleTextSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC07972552083500A3F79F /* ArticleTextSize.swift */; };
51DC079C2552083500A3F79F /* ArticleTextSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC07972552083500A3F79F /* ArticleTextSize.swift */; };
51DC07AC255209E200A3F79F /* ArticleTextSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC07972552083500A3F79F /* ArticleTextSize.swift */; };
51DC37072402153E0095D371 /* UpdateSelectionOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC37062402153E0095D371 /* UpdateSelectionOperation.swift */; };
51DC37092402F1470095D371 /* MasterFeedDataSourceOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC37082402F1470095D371 /* MasterFeedDataSourceOperation.swift */; };
51DC370B2405BC9A0095D371 /* PreloadedWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51DC370A2405BC9A0095D371 /* PreloadedWebView.swift */; };
@@ -1434,6 +1444,8 @@
/* Begin PBXFileReference section */
1704053324E5985A00A00787 /* SceneNavigationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneNavigationModel.swift; sourceTree = "<group>"; };
1710B9122552354E00679C0D /* AddAccountHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountHelpView.swift; sourceTree = "<group>"; };
1710B928255246F900679C0D /* EnableExtensionPointHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableExtensionPointHelpView.swift; sourceTree = "<group>"; };
1717535524BADF33004498C6 /* GeneralPreferencesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesModel.swift; sourceTree = "<group>"; };
171BCB8B24CB08A3006E22D9 /* FixAccountCredentialView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FixAccountCredentialView.swift; sourceTree = "<group>"; };
172199C824AB228900A31D04 /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
@@ -1730,6 +1742,7 @@
51CE1C0A23622006005548FC /* RefreshProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RefreshProgressView.swift; sourceTree = "<group>"; };
51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineDataSource.swift; sourceTree = "<group>"; };
51D87EE02311D34700E63F03 /* ActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityType.swift; sourceTree = "<group>"; };
51DC07972552083500A3F79F /* ArticleTextSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleTextSize.swift; sourceTree = "<group>"; };
51DC37062402153E0095D371 /* UpdateSelectionOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateSelectionOperation.swift; sourceTree = "<group>"; };
51DC37082402F1470095D371 /* MasterFeedDataSourceOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSourceOperation.swift; sourceTree = "<group>"; };
51DC370A2405BC9A0095D371 /* PreloadedWebView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreloadedWebView.swift; sourceTree = "<group>"; };
@@ -2304,6 +2317,7 @@
isa = PBXGroup;
children = (
5183CFAE254C78C8006B83A5 /* EnableExtensionPointView.swift */,
1710B928255246F900679C0D /* EnableExtensionPointHelpView.swift */,
515A516D243E7F950089E588 /* ExtensionPointDetail.xib */,
515A5170243E802B0089E588 /* ExtensionPointDetailViewController.swift */,
515A5147243E64BA0089E588 /* ExtensionPointEnableWindowController.swift */,
@@ -2804,11 +2818,12 @@
51C452A822650DA100C03939 /* Article Rendering */ = {
isa = PBXGroup;
children = (
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */,
B27EEBDF244D15F2000932E6 /* shared.css */,
848362FE2262A30E00DA1D35 /* template.html */,
517630032336215100E15FFF /* main.js */,
49F40DEF2335B71000552BF4 /* newsfoot.js */,
848362FE2262A30E00DA1D35 /* template.html */,
B27EEBDF244D15F2000932E6 /* shared.css */,
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */,
51DC07972552083500A3F79F /* ArticleTextSize.swift */,
);
path = "Article Rendering";
sourceTree = "<group>";
@@ -3328,6 +3343,7 @@
isa = PBXGroup;
children = (
178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */,
1710B9122552354E00679C0D /* AddAccountHelpView.swift */,
84C9FC7222629E1200D921D6 /* AccountsPreferencesViewController.swift */,
84C9FC7422629E1200D921D6 /* AccountsDetail.xib */,
5144EA2E2279FAB600D19003 /* AccountsDetailViewController.swift */,
@@ -4424,6 +4440,7 @@
511B149A24E5DC5400C919BD /* RefreshInterval.swift in Sources */,
510C418324E5D1B4008226FD /* ExtensionContainersFile.swift in Sources */,
510C418524E5D1B4008226FD /* ExtensionContainers.swift in Sources */,
51DC07AC255209E200A3F79F /* ArticleTextSize.swift in Sources */,
511B149824E5DC2300C919BD /* ShareDefaultContainer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -4485,6 +4502,7 @@
51E4996A24A8762D00B667CB /* ExtractedArticle.swift in Sources */,
51919FF124AB864A00541E64 /* TimelineModel.swift in Sources */,
5194736E24BBB937001A2939 /* HiddenModifier.swift in Sources */,
51DC079B2552083500A3F79F /* ArticleTextSize.swift in Sources */,
51E498F124A8085D00B667CB /* StarredFeedDelegate.swift in Sources */,
51E498FF24A808BB00B667CB /* SingleFaviconDownloader.swift in Sources */,
51E4997224A8784300B667CB /* DefaultFeedsImporter.swift in Sources */,
@@ -4668,6 +4686,7 @@
51E4992224A8095600B667CB /* URL-Extensions.swift in Sources */,
51E4990424A808C300B667CB /* WebFeedIconDownloader.swift in Sources */,
51E498CB24A8085D00B667CB /* TodayFeedDelegate.swift in Sources */,
51DC079C2552083500A3F79F /* ArticleTextSize.swift in Sources */,
51B80F1F24BE531200C6C32D /* SharingServiceView.swift in Sources */,
17D232A924AFF10A0005F075 /* AddWebFeedModel.swift in Sources */,
51A8005224CC453C00F41F1D /* ReplaySubject.swift in Sources */,
@@ -4794,11 +4813,13 @@
65ED3FBD235DEF6C0081F399 /* AppDefaults.swift in Sources */,
65ED3FBE235DEF6C0081F399 /* Account+Scriptability.swift in Sources */,
65ED3FBF235DEF6C0081F399 /* NothingInspectorViewController.swift in Sources */,
1710B92A255246F900679C0D /* EnableExtensionPointHelpView.swift in Sources */,
65ED3FC0235DEF6C0081F399 /* AppNotifications.swift in Sources */,
65ED3FC1235DEF6C0081F399 /* TimelineKeyboardDelegate.swift in Sources */,
65ED3FC2235DEF6C0081F399 /* Browser.swift in Sources */,
65ED3FC3235DEF6C0081F399 /* DetailWebViewController.swift in Sources */,
65ED3FC4235DEF6C0081F399 /* OPMLExporter.swift in Sources */,
51DC07992552083500A3F79F /* ArticleTextSize.swift in Sources */,
65ED3FC5235DEF6C0081F399 /* MainWindowController.swift in Sources */,
65ED3FC6235DEF6C0081F399 /* UnreadFeed.swift in Sources */,
65ED3FC8235DEF6C0081F399 /* SidebarCellLayout.swift in Sources */,
@@ -4863,6 +4884,7 @@
65ED3FF7235DEF6C0081F399 /* SearchFeedDelegate.swift in Sources */,
65ED3FF8235DEF6C0081F399 /* ErrorHandler.swift in Sources */,
65ED3FF9235DEF6C0081F399 /* ActivityManager.swift in Sources */,
1710B9142552354E00679C0D /* AddAccountHelpView.swift in Sources */,
65ED3FFA235DEF6C0081F399 /* WebFeedInspectorViewController.swift in Sources */,
65ED3FFB235DEF6C0081F399 /* AccountsReaderAPIWindowController.swift in Sources */,
65ED3FFC235DEF6C0081F399 /* AccountsAddLocalWindowController.swift in Sources */,
@@ -5049,6 +5071,7 @@
51C452AE2265104D00C03939 /* ArticleStringFormatter.swift in Sources */,
512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */,
51A9A60A2382FD240033AADF /* PoppableGestureRecognizerDelegate.swift in Sources */,
51DC079A2552083500A3F79F /* ArticleTextSize.swift in Sources */,
51C4529922650A0000C03939 /* ArticleStylesManager.swift in Sources */,
51EF0F802277A8330050506E /* MasterTimelineCellLayout.swift in Sources */,
51F85BF722749FA100C787DC /* UIFont-Extensions.swift in Sources */,
@@ -5179,6 +5202,7 @@
D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */,
8405DD9C22153BD7008CE1BF /* NSView-Extensions.swift in Sources */,
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */,
1710B929255246F900679C0D /* EnableExtensionPointHelpView.swift in Sources */,
515A50E6243D07A90089E588 /* ExtensionPointManager.swift in Sources */,
51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
515A5177243E90200089E588 /* ExtensionPointIdentifer.swift in Sources */,
@@ -5223,6 +5247,7 @@
5144EA382279FC6200D19003 /* AccountsAddLocalWindowController.swift in Sources */,
84AD1EAA2031617300BC20B7 /* PasteboardFolder.swift in Sources */,
515A5148243E64BA0089E588 /* ExtensionPointEnableWindowController.swift in Sources */,
51DC07982552083500A3F79F /* ArticleTextSize.swift in Sources */,
5117715524E1EA0F00A2A836 /* ArticleExtractorButton.swift in Sources */,
5103A9F724225E4C00410853 /* AccountsAddCloudKitWindowController.swift in Sources */,
5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */,
@@ -5234,6 +5259,7 @@
511B9806237DCAC90028BCAA /* UserInfoKey.swift in Sources */,
84C9FC7722629E1200D921D6 /* AdvancedPreferencesViewController.swift in Sources */,
849EE72120391F560082A1EA /* SharingServicePickerDelegate.swift in Sources */,
1710B9132552354E00679C0D /* AddAccountHelpView.swift in Sources */,
5108F6B62375E612001ABC45 /* CacheCleaner.swift in Sources */,
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
849EE70F203919360082A1EA /* AppAssets.swift in Sources */,

View File

@@ -69,8 +69,8 @@
"repositoryURL": "https://github.com/Ranchero-Software/RSParser.git",
"state": {
"branch": null,
"revision": "21d57ffb7ae744cf70bf6ddfb7ad8b7c102e05cf",
"version": "2.0.0-beta2"
"revision": "a4467cb6ab32d67fa8b09fcef8b234c7f96b7f9c",
"version": "2.0.0-beta3"
}
},
{

View File

@@ -52,8 +52,6 @@ extension AccountType {
return NSLocalizedString("NewsBlur", comment: "Account name")
case .theOldReader:
return NSLocalizedString("The Old Reader", comment: "Account name")
default:
return ""
}
}

View File

@@ -276,12 +276,7 @@ private extension ArticleRenderer {
#else
func styleSubstitutions() -> [String: String] {
var d = [String: String]()
if #available(macOS 11.0, *) {
let bodyFont = NSFont.preferredFont(forTextStyle: .body)
d["font-size"] = String(describing: Int(round(bodyFont.pointSize * 1.33)))
}
d["font-size"] = String(describing: AppDefaults.shared.articleTextSize.fontSize)
return d
}
#endif

View File

@@ -0,0 +1,52 @@
//
// ArticleTextSize.swift
// NetNewsWire
//
// Created by Maurice Parker on 11/3/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import Foundation
enum ArticleTextSize: Int, CaseIterable, Identifiable {
case small = 1
case medium = 2
case large = 3
case xlarge = 4
case xxlarge = 5
#if os(macOS)
var fontSize: Int {
switch self {
case .small:
return 14
case .medium:
return 16
case .large:
return 18
case .xlarge:
return 20
case .xxlarge:
return 22
}
}
#endif
var id: String { description() }
func description() -> String {
switch self {
case .small:
return NSLocalizedString("Small", comment: "Small")
case .medium:
return NSLocalizedString("Medium", comment: "Medium")
case .large:
return NSLocalizedString("Large", comment: "Large")
case .xlarge:
return NSLocalizedString("X-Large", comment: "X-Large")
case .xxlarge:
return NSLocalizedString("XX-Large", comment: "XX-Large")
}
}
}

View File

@@ -16,7 +16,7 @@ extension RedditFeedProvider: ExtensionPoint {
static var title = NSLocalizedString("Reddit", comment: "Reddit")
static var image = AppAssets.extensionPointReddit
static var description: NSAttributedString = {
return RedditFeedProvider.makeAttrString("This extension enables you to subscribe to Reddit URL's as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.")
return RedditFeedProvider.makeAttrString("This extension enables you to subscribe to Reddit URLs as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.")
}()
var extensionPointID: ExtensionPointIdentifer {

View File

@@ -17,7 +17,7 @@ final class SendToMarsEditCommand: ExtensionPoint, SendToCommand {
static var title = NSLocalizedString("MarsEdit", comment: "MarsEdit")
static var image = AppAssets.extensionPointMarsEdit
static var description: NSAttributedString = {
let attrString = SendToMarsEditCommand.makeAttrString("This extension enables share menu functionality to send selected article text to MarsEdit. You need the MarsEdit application for this to work.")
let attrString = SendToMarsEditCommand.makeAttrString("This extension enables share menu functionality to send selected article text to MarsEdit. You need the MarsEdit application for this to work.")
let range = NSRange(location: 81, length: 8)
attrString.beginEditing()
attrString.addAttribute(NSAttributedString.Key.link, value: "https://red-sweater.com/marsedit/", range: range)

View File

@@ -19,7 +19,7 @@ final class SendToMicroBlogCommand: ExtensionPoint, SendToCommand {
static var title: String = NSLocalizedString("Micro.blog", comment: "Micro.blog")
static var image = AppAssets.extensionPointMicroblog
static var description: NSAttributedString = {
let attrString = SendToMicroBlogCommand.makeAttrString("This extension enables share menu functionality to send selected article text to Micro.blog. You need the Micro.blog application for this to work.")
let attrString = SendToMicroBlogCommand.makeAttrString("This extension enables share menu functionality to send selected article text to Micro.blog. You need the Micro.blog application for this to work.")
let range = NSRange(location: 81, length: 10)
attrString.beginEditing()
attrString.addAttribute(NSAttributedString.Key.link, value: "https://micro.blog", range: range)

View File

@@ -16,7 +16,7 @@ extension TwitterFeedProvider: ExtensionPoint {
static var title = NSLocalizedString("Twitter", comment: "Twitter")
static var image = AppAssets.extensionPointTwitter
static var description: NSAttributedString = {
return TwitterFeedProvider.makeAttrString("This extension enables you to subscribe to Twitter URL's as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.")
return TwitterFeedProvider.makeAttrString("This extension enables you to subscribe to Twitter URLs as if they were RSS feeds. It only works with \(Account.defaultLocalAccountName) or iCloud accounts.")
}()
var extensionPointID: ExtensionPointIdentifer {

View File

@@ -20,18 +20,21 @@ extension Array where Element == Article {
return self[row]
}
func rowOfNextUnreadArticle(_ selectedRow: Int) -> Int? {
func orderedRowIndexes(fromIndex startIndex: Int, wrappingToTop wrapping: Bool) -> [Int] {
if startIndex >= self.count {
// Wrap around to the top if specified
return wrapping ? Array<Int>(0..<self.count) : []
} else {
// Start at the selection and wrap around to the beginning
return Array<Int>(startIndex..<self.count) + (wrapping ? Array<Int>(0..<startIndex) : [])
}
}
func rowOfNextUnreadArticle(_ selectedRow: Int, wrappingToTop wrapping: Bool) -> Int? {
if isEmpty {
return nil
}
var rowIndex = selectedRow
while(true) {
rowIndex = rowIndex + 1
if rowIndex >= count {
break
}
for rowIndex in orderedRowIndexes(fromIndex: selectedRow + 1, wrappingToTop: wrapping) {
let article = articleAtRow(rowIndex)!
if !article.status.read {
return rowIndex

View File

@@ -627,7 +627,7 @@
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="API URL" textAlignment="natural" adjustsFontForContentSizeCategory="YES" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="iPv-M2-U8Q">
<rect key="frame" x="14" y="11" width="340" height="22"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" textContentType="url"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="URL" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no" textContentType="url"/>
</textField>
</subviews>
<constraints>

View File

@@ -82,7 +82,7 @@ class FeedbinAccountViewController: UITableViewController {
// When you fill in the email address via auto-complete it adds extra whitespace
let trimmedEmail = email.trimmingCharacters(in: .whitespaces)
guard !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: trimmedEmail) else {
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: trimmedEmail) else {
showError(NSLocalizedString("There is already a Feedbin account with that username created.", comment: "Duplicate Error"))
return
}

View File

@@ -154,7 +154,7 @@ struct AppAssets {
}()
static var masterFolderImage: IconImage = {
return IconImage(UIImage(systemName: "folder.fill")!)
return IconImage(UIImage(systemName: "folder.fill")!, isSymbol: true)
}()
static var moreImage: UIImage = {
@@ -186,7 +186,7 @@ struct AppAssets {
}()
static var searchFeedImage: IconImage = {
return IconImage(UIImage(systemName: "magnifyingglass")!)
return IconImage(UIImage(systemName: "magnifyingglass")!, isSymbol: true)
}()
static var secondaryAccentColor: UIColor = {

View File

@@ -52,7 +52,7 @@ final class IconView: UIView {
}
private var isSymbolImage: Bool {
return imageView.image?.isSymbolImage ?? false
return iconImage?.isSymbol ?? false
}
override init(frame: CGRect) {
@@ -75,7 +75,7 @@ final class IconView: UIView {
override func layoutSubviews() {
imageView.setFrameIfNotEqual(rectForImageView())
if (iconImage != nil && isVerticalBackgroundExposed && !isSymbolImage) || !isDisconcernable {
if !isSymbolImage && ((iconImage != nil && isVerticalBackgroundExposed) || !isDisconcernable) {
backgroundColor = AppAssets.iconBackgroundColor
} else {
backgroundColor = nil

View File

@@ -18,13 +18,16 @@ extension MasterFeedViewController: UITableViewDropDelegate {
}
func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {
guard let destIndexPath = destinationIndexPath,
destIndexPath.section > 0,
tableView.hasActiveDrag,
let destIdentifier = dataSource.itemIdentifier(for: destIndexPath),
let destAccount = destIdentifier.account,
let destCell = tableView.cellForRow(at: destIndexPath) else {
return UITableViewDropProposal(operation: .forbidden)
guard let destIndexPath = destinationIndexPath, destIndexPath.section > 0, tableView.hasActiveDrag else {
return UITableViewDropProposal(operation: .forbidden)
}
guard let destIdentifier = dataSource.itemIdentifier(for: destIndexPath) else {
return UITableViewDropProposal(operation: .move, intent: .insertIntoDestinationIndexPath)
}
guard let destAccount = destIdentifier.account, let destCell = tableView.cellForRow(at: destIndexPath) else {
return UITableViewDropProposal(operation: .forbidden)
}
// Validate account specific behaviors...
@@ -90,7 +93,8 @@ extension MasterFeedViewController: UITableViewDropDelegate {
if let containerID = destIdentifier?.containerID ?? destIdentifier?.parentContainerID {
return AccountManager.shared.existingContainer(with: containerID)
} else {
return nil
// If we got here, we are trying to drop on an empty section header. Go and find the Account for this section
return coordinator.rootNode.childAtIndex(destIndexPath.section)?.representedObject as? Account
}
}()

View File

@@ -337,8 +337,8 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
return proposedDestinationIndexPath
}
// If this is a folder and isn't expanded or doesn't have any entries, let the users drop on it
if destNode.representedObject is Folder && (destNode.numberOfChildNodes == 0 || !coordinator.isExpanded(destNode)) {
// If this is a folder, let the users drop on it
if destNode.representedObject is Folder {
return proposedDestinationIndexPath
}

View File

@@ -114,11 +114,11 @@ private extension SceneDelegate {
DispatchQueue.main.async {
switch AppDefaults.userInterfaceColorPalette {
case .automatic:
self.window!.overrideUserInterfaceStyle = .unspecified
self.window?.overrideUserInterfaceStyle = .unspecified
case .light:
self.window!.overrideUserInterfaceStyle = .light
self.window?.overrideUserInterfaceStyle = .light
case .dark:
self.window!.overrideUserInterfaceStyle = .dark
self.window?.overrideUserInterfaceStyle = .dark
}
}
}

View File

@@ -41,6 +41,6 @@ PRODUCT_BUNDLE_IDENTIFIER = $(ORGANIZATION_IDENTIFIER).NetNewsWire-Evergreen
PRODUCT_NAME = NetNewsWire
// Override NetNewsWire_project.xcconfig until we are ready to only target 10.16
MACOSX_DEPLOYMENT_TARGET = 10.16
MACOSX_DEPLOYMENT_TARGET = 11.0
SWIFT_SWIFT3_OBJC_INFERENCE = Off
SWIFT_VERSION = 5.3

View File

@@ -1,7 +1,7 @@
// High Level Settings common to both the iOS application and any extensions we bundle with it
MARKETING_VERSION = 5.0.4
CURRENT_PROJECT_VERSION = 53
MARKETING_VERSION = 5.0.5
CURRENT_PROJECT_VERSION = 56
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon

View File

@@ -3,6 +3,7 @@ MARKETING_VERSION = 5.1.2
CURRENT_PROJECT_VERSION = 3016
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
COMBINE_HIDPI_IMAGES = YES
MACOSX_DEPLOYMENT_TARGET = 10.15