Merge branch 'main' of https://github.com/Ranchero-Software/NetNewsWire into main
@ -17,7 +17,9 @@ import ArticlesDatabase
|
||||
public final class AccountManager: UnreadCountProvider {
|
||||
|
||||
public static var shared: AccountManager!
|
||||
|
||||
public static let netNewsWireNewsURL = "https://nnw.ranchero.com/feed.xml"
|
||||
private static let jsonNetNewsWireNewsURL = "https://nnw.ranchero.com/feed.json"
|
||||
|
||||
public let defaultAccount: Account
|
||||
|
||||
private let accountsFolder: String
|
||||
@ -319,6 +321,10 @@ public final class AccountManager: UnreadCountProvider {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public func anyAccountHasNetNewsWireNewsSubscription() -> Bool {
|
||||
return anyAccountHasFeedWithURL(Self.netNewsWireNewsURL) || anyAccountHasFeedWithURL(Self.jsonNetNewsWireNewsURL)
|
||||
}
|
||||
|
||||
public func anyAccountHasFeedWithURL(_ urlString: String) -> Bool {
|
||||
for account in activeAccounts {
|
||||
|
@ -95,18 +95,25 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
||||
refreshAccount(account) { result in
|
||||
switch result {
|
||||
case .success():
|
||||
|
||||
self.sendArticleStatus(for: account) { _ in
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshArticleStatus(for: account) { _ in
|
||||
self.caller.retrieveItemIDs(type: .allForAccount) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshArticles(account) {
|
||||
self.refreshMissingArticles(account) {
|
||||
self.refreshProgress.clear()
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(()))
|
||||
switch result {
|
||||
case .success(let articleIDs):
|
||||
account.markAsRead(Set(articleIDs)) { _ in
|
||||
self.refreshArticleStatus(for: account) { _ in
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshMissingArticles(account) {
|
||||
self.refreshProgress.clear()
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,10 +180,10 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
||||
|
||||
func refreshArticleStatus(for account: Account, completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
os_log(.debug, log: log, "Refreshing article statuses...")
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
group.enter()
|
||||
caller.retrieveUnreadEntries() { result in
|
||||
caller.retrieveItemIDs(type: .unread) { result in
|
||||
switch result {
|
||||
case .success(let articleIDs):
|
||||
self.syncArticleReadState(account: account, articleIDs: articleIDs)
|
||||
@ -189,7 +196,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
||||
}
|
||||
|
||||
group.enter()
|
||||
caller.retrieveStarredEntries() { result in
|
||||
caller.retrieveItemIDs(type: .starred) { result in
|
||||
switch result {
|
||||
case .success(let articleIDs):
|
||||
self.syncArticleStarredState(account: account, articleIDs: articleIDs)
|
||||
@ -403,7 +410,6 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
||||
}
|
||||
|
||||
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
if let folder = container as? Folder, let feedName = feed.externalID {
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
caller.createTagging(subscriptionID: feedName, tagName: folder.name ?? "") { result in
|
||||
@ -431,7 +437,6 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
@ -560,7 +565,6 @@ private extension ReaderAPIAccountDelegate {
|
||||
self.syncFolders(account, tags)
|
||||
}
|
||||
self.refreshProgress.completeTask()
|
||||
self.forceExpireFolderFeedRelationship(account, tags)
|
||||
self.refreshFeeds(account, completion: completion)
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
@ -568,30 +572,6 @@ private extension ReaderAPIAccountDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func forceExpireFolderFeedRelationship(_ account: Account, _ tags: [ReaderAPITag]?) {
|
||||
guard let tags = tags else { return }
|
||||
|
||||
let folderNames: [String] = {
|
||||
if let folders = account.folders {
|
||||
return folders.map { $0.name ?? "" }
|
||||
} else {
|
||||
return [String]()
|
||||
}
|
||||
}()
|
||||
|
||||
let readerFolderNames = tags.compactMap { $0.folderName }
|
||||
|
||||
// The sync service has a tag that we don't have a folder for. We might not get a new
|
||||
// taggings response for it if it is a folder rename. Force expire the subscription
|
||||
// so that we will for sure get the new tagging information by pulling all subscriptions.
|
||||
readerFolderNames.forEach { tagName in
|
||||
if !folderNames.contains(tagName) {
|
||||
accountMetadata?.conditionalGetInfo[ReaderAPICaller.ConditionalGetKeys.subscriptions] = nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func syncFolders(_ account: Account, _ tags: [ReaderAPITag]?) {
|
||||
guard let tags = tags else { return }
|
||||
assert(Thread.isMainThread)
|
||||
@ -879,30 +859,21 @@ private extension ReaderAPIAccountDelegate {
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||
|
||||
// Download the initial articles
|
||||
self.caller.retrieveEntries(webFeedID: feed.webFeedID) { result in
|
||||
self.caller.retrieveItemIDs(type: .allForFeed, webFeedID: feed.webFeedID) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success(let (entries, page)):
|
||||
self.processEntries(account: account, entries: entries) {
|
||||
|
||||
case .success(let articleIDs):
|
||||
account.markAsRead(Set(articleIDs)) { _ in
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshArticleStatus(for: account) { _ in
|
||||
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshArticles(account, page: page) {
|
||||
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshMissingArticles(account) {
|
||||
|
||||
self.refreshProgress.clear()
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(feed))
|
||||
}
|
||||
|
||||
self.refreshMissingArticles(account) {
|
||||
self.refreshProgress.clear()
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(feed))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -914,29 +885,6 @@ private extension ReaderAPIAccountDelegate {
|
||||
|
||||
}
|
||||
|
||||
func refreshArticles(_ account: Account, completion: @escaping (() -> Void)) {
|
||||
os_log(.debug, log: log, "Refreshing articles...")
|
||||
|
||||
caller.retrieveEntries() { result in
|
||||
switch result {
|
||||
case .success(let (entries, page, lastPageNumber)):
|
||||
if let last = lastPageNumber {
|
||||
self.refreshProgress.addToNumberOfTasksAndRemaining(last - 1)
|
||||
}
|
||||
self.processEntries(account: account, entries: entries) {
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshArticles(account, page: page) {
|
||||
os_log(.debug, log: self.log, "Done refreshing articles.")
|
||||
completion()
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
os_log(.error, log: self.log, "Refresh articles failed: %@.", error.localizedDescription)
|
||||
completion()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func refreshMissingArticles(_ account: Account, completion: @escaping VoidCompletionBlock) {
|
||||
account.fetchArticleIDsForStatusesWithoutArticlesNewerThanCutoffDate { articleIDsResult in
|
||||
|
||||
@ -981,32 +929,6 @@ private extension ReaderAPIAccountDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func refreshArticles(_ account: Account, page: String?, completion: @escaping (() -> Void)) {
|
||||
|
||||
guard let page = page else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
caller.retrieveEntries(page: page) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (entries, nextPage)):
|
||||
|
||||
self.processEntries(account: account, entries: entries) {
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshArticles(account, page: nextPage, completion: completion)
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
os_log(.error, log: self.log, "Refresh articles for additional pages failed: %@.", error.localizedDescription)
|
||||
completion()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func processEntries(account: Account, entries: [ReaderAPIEntry]?, completion: @escaping VoidCompletionBlock) {
|
||||
let parsedItems = mapEntriesToParsedItems(account: account, entries: entries)
|
||||
let webFeedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
|
||||
|
@ -18,23 +18,23 @@ enum CreateReaderAPISubscriptionResult {
|
||||
|
||||
final class ReaderAPICaller: NSObject {
|
||||
|
||||
struct ConditionalGetKeys {
|
||||
static let subscriptions = "subscriptions"
|
||||
static let tags = "tags"
|
||||
static let unreadEntries = "unreadEntries"
|
||||
static let starredEntries = "starredEntries"
|
||||
enum ItemIDType {
|
||||
case unread
|
||||
case starred
|
||||
case allForAccount
|
||||
case allForFeed
|
||||
}
|
||||
|
||||
enum ReaderState: String {
|
||||
private enum ReaderState: String {
|
||||
case read = "user/-/state/com.google/read"
|
||||
case starred = "user/-/state/com.google/starred"
|
||||
}
|
||||
|
||||
enum ReaderStreams: String {
|
||||
private enum ReaderStreams: String {
|
||||
case readingList = "user/-/state/com.google/reading-list"
|
||||
}
|
||||
|
||||
enum ReaderAPIEndpoints: String {
|
||||
private enum ReaderAPIEndpoints: String {
|
||||
case login = "/accounts/ClientLogin"
|
||||
case token = "/reader/api/0/token"
|
||||
case disableTag = "/reader/api/0/disable-tag"
|
||||
@ -184,20 +184,16 @@ final class ReaderAPICaller: NSObject {
|
||||
return
|
||||
}
|
||||
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.tags]
|
||||
var request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
var request = URLRequest(url: callURL, credentials: credentials)
|
||||
addVariantHeaders(&request)
|
||||
|
||||
transport.send(request: request, resultType: ReaderAPITagContainer.self) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (response, wrapper)):
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.tags, headers: response.allHeaderFields)
|
||||
case .success(let (_, wrapper)):
|
||||
completion(.success(wrapper?.tags))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -292,22 +288,17 @@ final class ReaderAPICaller: NSObject {
|
||||
return
|
||||
}
|
||||
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.subscriptions]
|
||||
var request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
var request = URLRequest(url: callURL, credentials: credentials)
|
||||
addVariantHeaders(&request)
|
||||
|
||||
transport.send(request: request, resultType: ReaderAPISubscriptionContainer.self) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (response, container)):
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.subscriptions, headers: response.allHeaderFields)
|
||||
case .success(let (_, container)):
|
||||
completion(.success(container?.subscriptions))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func createSubscription(url: String, name: String?, folder: Folder?, completion: @escaping (Result<CreateReaderAPISubscriptionResult, Error>) -> Void) {
|
||||
@ -576,297 +567,188 @@ final class ReaderAPICaller: NSObject {
|
||||
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
request.httpMethod = "POST"
|
||||
|
||||
let chunkedArticleIds = articleIDs.chunked(into: 200)
|
||||
let group = DispatchGroup()
|
||||
var groupEntries = [ReaderAPIEntry]()
|
||||
var groupError: Error? = nil
|
||||
|
||||
for articleIDChunk in chunkedArticleIds {
|
||||
let itemFetchParameters = articleIDChunk.map({ articleID -> String in
|
||||
return "i=tag:google.com,2005:reader/item/\(articleID)"
|
||||
}).joined(separator:"&")
|
||||
|
||||
let postData = "T=\(token)&output=json&\(itemFetchParameters)".data(using: String.Encoding.utf8)
|
||||
|
||||
group.enter()
|
||||
self.transport.send(request: request, method: HTTPMethod.post, data: postData!, resultType: ReaderAPIEntryWrapper.self, completion: { (result) in
|
||||
switch result {
|
||||
case .success(let (_, entryWrapper)):
|
||||
guard let entryWrapper = entryWrapper else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidResponse))
|
||||
return
|
||||
}
|
||||
groupEntries.append(contentsOf: entryWrapper.entries)
|
||||
group.leave()
|
||||
case .failure(let error):
|
||||
groupError = error
|
||||
group.leave()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
if let error = groupError {
|
||||
completion(.failure(error))
|
||||
} else {
|
||||
completion(.success(groupEntries))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func retrieveEntries(webFeedID: String, completion: @escaping (Result<([ReaderAPIEntry]?, String?), Error>) -> Void) {
|
||||
|
||||
let since = Calendar.current.date(byAdding: .month, value: -3, to: Date()) ?? Date()
|
||||
|
||||
guard let baseURL = APIBaseURL else {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
let url = baseURL
|
||||
.appendingPathComponent(ReaderAPIEndpoints.itemIds.rawValue)
|
||||
.appendingQueryItems([
|
||||
URLQueryItem(name: "s", value: webFeedID),
|
||||
URLQueryItem(name: "ot", value: String(Int(since.timeIntervalSince1970))),
|
||||
URLQueryItem(name: "output", value: "json")
|
||||
])
|
||||
|
||||
guard let callURL = url else {
|
||||
completion(.failure(TransportError.noURL))
|
||||
return
|
||||
}
|
||||
|
||||
var request = URLRequest(url: callURL, credentials: credentials, conditionalGet: nil)
|
||||
addVariantHeaders(&request)
|
||||
|
||||
transport.send(request: request, resultType: ReaderAPIReferenceWrapper.self) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (_, unreadEntries)):
|
||||
|
||||
guard let itemRefs = unreadEntries?.itemRefs else {
|
||||
completion(.success(([], nil)))
|
||||
return
|
||||
}
|
||||
|
||||
let itemIds = itemRefs.map { (reference) -> String in
|
||||
// Get ids from above into hex representation of value
|
||||
let idsToFetch = articleIDs.map({ articleID -> String in
|
||||
if self.variant == .theOldReader {
|
||||
return reference.itemId
|
||||
return "i=tag:google.com,2005:reader/item/\(articleID)"
|
||||
} else {
|
||||
// Convert the IDs to the (stupid) Google Hex Format
|
||||
let idValue = Int(reference.itemId)!
|
||||
return String(idValue, radix: 16, uppercase: false)
|
||||
let idValue = Int(articleID)!
|
||||
let idHexString = String(idValue, radix: 16, uppercase: false)
|
||||
return "i=tag:google.com,2005:reader/item/\(idHexString)"
|
||||
}
|
||||
}
|
||||
}).joined(separator:"&")
|
||||
|
||||
self.retrieveEntries(articleIDs: itemIds) { (results) in
|
||||
switch results {
|
||||
case .success(let entries):
|
||||
completion(.success((entries,nil)))
|
||||
let postData = "T=\(token)&output=json&\(idsToFetch)".data(using: String.Encoding.utf8)
|
||||
|
||||
self.transport.send(request: request, method: HTTPMethod.post, data: postData!, resultType: ReaderAPIEntryWrapper.self, completion: { (result) in
|
||||
switch result {
|
||||
case .success(let (_, entryWrapper)):
|
||||
guard let entryWrapper = entryWrapper else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidResponse))
|
||||
return
|
||||
}
|
||||
|
||||
completion(.success((entryWrapper.entries)))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func retrieveEntries(completion: @escaping (Result<([ReaderAPIEntry]?, String?, Int?), Error>) -> Void) {
|
||||
|
||||
}
|
||||
|
||||
func retrieveItemIDs(type: ItemIDType, webFeedID: String? = nil, completion: @escaping ((Result<[String], Error>) -> Void)) {
|
||||
guard let baseURL = APIBaseURL else {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
let since: Date = {
|
||||
if let lastArticleFetch = self.accountMetadata?.lastArticleFetchStartTime {
|
||||
return lastArticleFetch
|
||||
} else {
|
||||
return Calendar.current.date(byAdding: .month, value: -3, to: Date()) ?? Date()
|
||||
}
|
||||
}()
|
||||
var queryItems = [
|
||||
URLQueryItem(name: "n", value: "1000"),
|
||||
URLQueryItem(name: "output", value: "json")
|
||||
]
|
||||
|
||||
switch type {
|
||||
case .allForAccount:
|
||||
let since: Date = {
|
||||
if let lastArticleFetch = self.accountMetadata?.lastArticleFetchStartTime {
|
||||
return lastArticleFetch
|
||||
} else {
|
||||
return Calendar.current.date(byAdding: .month, value: -3, to: Date()) ?? Date()
|
||||
}
|
||||
}()
|
||||
|
||||
let sinceTimeInterval = since.timeIntervalSince1970
|
||||
queryItems.append(URLQueryItem(name: "ot", value: String(Int(sinceTimeInterval))))
|
||||
queryItems.append(URLQueryItem(name: "s", value: ReaderStreams.readingList.rawValue))
|
||||
case .allForFeed:
|
||||
guard let webFeedID = webFeedID else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
let sinceTimeInterval = (Calendar.current.date(byAdding: .month, value: -3, to: Date()) ?? Date()).timeIntervalSince1970
|
||||
queryItems.append(URLQueryItem(name: "ot", value: String(Int(sinceTimeInterval))))
|
||||
queryItems.append(URLQueryItem(name: "s", value: webFeedID))
|
||||
case .unread:
|
||||
queryItems.append(URLQueryItem(name: "s", value: ReaderStreams.readingList.rawValue))
|
||||
queryItems.append(URLQueryItem(name: "xt", value: ReaderState.read.rawValue))
|
||||
case .starred:
|
||||
queryItems.append(URLQueryItem(name: "s", value: ReaderState.starred.rawValue))
|
||||
}
|
||||
|
||||
let sinceTimeInterval = since.timeIntervalSince1970
|
||||
let url = baseURL
|
||||
.appendingPathComponent(ReaderAPIEndpoints.itemIds.rawValue)
|
||||
.appendingQueryItems([
|
||||
URLQueryItem(name: "ot", value: String(Int(sinceTimeInterval))),
|
||||
URLQueryItem(name: "n", value: "1000"),
|
||||
URLQueryItem(name: "output", value: "json"),
|
||||
URLQueryItem(name: "s", value: ReaderStreams.readingList.rawValue)
|
||||
])
|
||||
.appendingQueryItems(queryItems)
|
||||
|
||||
guard let callURL = url else {
|
||||
completion(.failure(TransportError.noURL))
|
||||
return
|
||||
}
|
||||
|
||||
var request = URLRequest(url: callURL, credentials: credentials)
|
||||
var request: URLRequest = URLRequest(url: callURL, credentials: credentials)
|
||||
addVariantHeaders(&request)
|
||||
|
||||
self.transport.send(request: request, resultType: ReaderAPIReferenceWrapper.self) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (response, entries)):
|
||||
|
||||
guard let entriesItemRefs = entries?.itemRefs, entriesItemRefs.count > 0 else {
|
||||
completion(.success((nil, nil, nil)))
|
||||
completion(.success([String]()))
|
||||
return
|
||||
}
|
||||
|
||||
// This needs to be moved when we fix paging for item ids
|
||||
let dateInfo = HTTPDateInfo(urlResponse: response)
|
||||
let itemIDs = entriesItemRefs.compactMap { $0.itemId }
|
||||
self.retrieveItemIDs(type: type, url: callURL, dateInfo: dateInfo, itemIDs: itemIDs, continuation: entries?.continuation, completion: completion)
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveItemIDs(type: ItemIDType, url: URL, dateInfo: HTTPDateInfo?, itemIDs: [String], continuation: String?, completion: @escaping ((Result<[String], Error>) -> Void)) {
|
||||
guard let continuation = continuation else {
|
||||
if type == .allForAccount {
|
||||
self.accountMetadata?.lastArticleFetchStartTime = dateInfo?.date
|
||||
self.accountMetadata?.lastArticleFetchEndTime = Date()
|
||||
|
||||
self.requestAuthorizationToken(endpoint: baseURL) { (result) in
|
||||
switch result {
|
||||
case .success(let token):
|
||||
var request = URLRequest(url: baseURL.appendingPathComponent(ReaderAPIEndpoints.contents.rawValue), credentials: self.credentials)
|
||||
self.addVariantHeaders(&request)
|
||||
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
request.httpMethod = "POST"
|
||||
|
||||
let chunkedItemRefs = entriesItemRefs.chunked(into: 200)
|
||||
let group = DispatchGroup()
|
||||
var groupEntries = [ReaderAPIEntry]()
|
||||
var groupError: Error? = nil
|
||||
|
||||
for itemRefsChunk in chunkedItemRefs {
|
||||
let itemFetchParameters = itemRefsChunk.map({ itemRef -> String in
|
||||
if self.variant == .theOldReader {
|
||||
return "i=tag:google.com,2005:reader/item/\(itemRef.itemId)"
|
||||
} else {
|
||||
let idValue = Int(itemRef.itemId)!
|
||||
let idHexString = String(idValue, radix: 16, uppercase: false)
|
||||
return "i=tag:google.com,2005:reader/item/\(idHexString)"
|
||||
}
|
||||
}).joined(separator:"&")
|
||||
|
||||
let postData = "T=\(token)&output=json&\(itemFetchParameters)".data(using: String.Encoding.utf8)
|
||||
|
||||
group.enter()
|
||||
self.transport.send(request: request, method: HTTPMethod.post, data: postData!, resultType: ReaderAPIEntryWrapper.self, completion: { (result) in
|
||||
switch result {
|
||||
case .success(let (_, entryWrapper)):
|
||||
guard let entryWrapper = entryWrapper else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidResponse))
|
||||
return
|
||||
}
|
||||
groupEntries.append(contentsOf: entryWrapper.entries)
|
||||
group.leave()
|
||||
case .failure(let error):
|
||||
groupError = error
|
||||
group.leave()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) {
|
||||
if let error = groupError {
|
||||
completion(.failure(error))
|
||||
} else {
|
||||
completion(.success((groupEntries, nil, nil)))
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
self.accountMetadata?.lastArticleFetchStartTime = nil
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func retrieveEntries(page: String, completion: @escaping (Result<([ReaderAPIEntry]?, String?), Error>) -> Void) {
|
||||
|
||||
guard let url = URL(string: page)?.appendingQueryItem(URLQueryItem(name: "mode", value: "extended")) else {
|
||||
completion(.success((nil, nil)))
|
||||
return
|
||||
}
|
||||
var request = URLRequest(url: url, credentials: credentials)
|
||||
addVariantHeaders(&request)
|
||||
|
||||
transport.send(request: request, resultType: [ReaderAPIEntry].self) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (response, entries)):
|
||||
|
||||
let pagingInfo = HTTPLinkPagingInfo(urlResponse: response)
|
||||
completion(.success((entries, pagingInfo.nextPage)))
|
||||
|
||||
case .failure(let error):
|
||||
self.accountMetadata?.lastArticleFetchStartTime = nil
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func retrieveUnreadEntries(completion: @escaping (Result<[String]?, Error>) -> Void) {
|
||||
|
||||
guard let baseURL = APIBaseURL else {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
completion(.success(itemIDs))
|
||||
return
|
||||
}
|
||||
|
||||
let url = baseURL
|
||||
.appendingPathComponent(ReaderAPIEndpoints.itemIds.rawValue)
|
||||
.appendingQueryItems([
|
||||
URLQueryItem(name: "s", value: ReaderStreams.readingList.rawValue),
|
||||
URLQueryItem(name: "n", value: "1000"),
|
||||
URLQueryItem(name: "xt", value: ReaderState.read.rawValue),
|
||||
URLQueryItem(name: "output", value: "json")
|
||||
])
|
||||
guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
guard let callURL = url else {
|
||||
var queryItems = urlComponents.queryItems!.filter({ $0.name != "c" })
|
||||
queryItems.append(URLQueryItem(name: "c", value: continuation))
|
||||
urlComponents.queryItems = queryItems
|
||||
|
||||
guard let callURL = urlComponents.url else {
|
||||
completion(.failure(TransportError.noURL))
|
||||
return
|
||||
}
|
||||
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.unreadEntries]
|
||||
var request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
|
||||
var request: URLRequest = URLRequest(url: callURL, credentials: credentials)
|
||||
addVariantHeaders(&request)
|
||||
|
||||
transport.send(request: request, resultType: ReaderAPIReferenceWrapper.self) { result in
|
||||
|
||||
self.transport.send(request: request, resultType: ReaderAPIReferenceWrapper.self) { result in
|
||||
switch result {
|
||||
case .success(let (response, unreadEntries)):
|
||||
|
||||
guard let itemRefs = unreadEntries?.itemRefs else {
|
||||
completion(.success([]))
|
||||
case .success(let (_, entries)):
|
||||
guard let entriesItemRefs = entries?.itemRefs, entriesItemRefs.count > 0 else {
|
||||
self.retrieveItemIDs(type: type, url: callURL, dateInfo: dateInfo, itemIDs: itemIDs, continuation: entries?.continuation, completion: completion)
|
||||
return
|
||||
}
|
||||
|
||||
let itemIds = itemRefs.map { $0.itemId }
|
||||
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.unreadEntries, headers: response.allHeaderFields)
|
||||
completion(.success(itemIds))
|
||||
var totalItemIDs = itemIDs
|
||||
totalItemIDs.append(contentsOf: entriesItemRefs.compactMap { $0.itemId })
|
||||
self.retrieveItemIDs(type: type, url: callURL, dateInfo: dateInfo, itemIDs: totalItemIDs, continuation: entries?.continuation, completion: completion)
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func updateStateToEntries(entries: [String], state: ReaderState, add: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
func createUnreadEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .read, add: false, completion: completion)
|
||||
}
|
||||
|
||||
func deleteUnreadEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .read, add: true, completion: completion)
|
||||
}
|
||||
|
||||
func createStarredEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .starred, add: true, completion: completion)
|
||||
}
|
||||
|
||||
func deleteStarredEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .starred, add: false, completion: completion)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension ReaderAPICaller {
|
||||
|
||||
func storeConditionalGet(key: String, headers: [AnyHashable : Any]) {
|
||||
if var conditionalGet = accountMetadata?.conditionalGetInfo {
|
||||
conditionalGet[key] = HTTPConditionalGetInfo(headers: headers)
|
||||
accountMetadata?.conditionalGetInfo = conditionalGet
|
||||
}
|
||||
}
|
||||
|
||||
func addVariantHeaders(_ request: inout URLRequest) {
|
||||
if variant == .inoreader {
|
||||
request.addValue(SecretsManager.provider.inoreaderAppId, forHTTPHeaderField: "AppId")
|
||||
request.addValue(SecretsManager.provider.inoreaderAppKey, forHTTPHeaderField: "AppKey")
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStateToEntries(entries: [String], state: ReaderState, add: Bool, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let baseURL = APIBaseURL else {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
return
|
||||
@ -912,85 +794,5 @@ final class ReaderAPICaller: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
func createUnreadEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .read, add: false, completion: completion)
|
||||
}
|
||||
|
||||
func deleteUnreadEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .read, add: true, completion: completion)
|
||||
|
||||
}
|
||||
|
||||
func createStarredEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .starred, add: true, completion: completion)
|
||||
|
||||
}
|
||||
|
||||
func deleteStarredEntries(entries: [String], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
updateStateToEntries(entries: entries, state: .starred, add: false, completion: completion)
|
||||
}
|
||||
|
||||
func retrieveStarredEntries(completion: @escaping (Result<[String]?, Error>) -> Void) {
|
||||
guard let baseURL = APIBaseURL else {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
return
|
||||
}
|
||||
|
||||
let url = baseURL
|
||||
.appendingPathComponent(ReaderAPIEndpoints.itemIds.rawValue)
|
||||
.appendingQueryItems([
|
||||
URLQueryItem(name: "s", value: ReaderState.starred.rawValue),
|
||||
URLQueryItem(name: "n", value: "1000"),
|
||||
URLQueryItem(name: "output", value: "json")
|
||||
])
|
||||
|
||||
guard let callURL = url else {
|
||||
completion(.failure(TransportError.noURL))
|
||||
return
|
||||
}
|
||||
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.starredEntries]
|
||||
var request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
addVariantHeaders(&request)
|
||||
|
||||
transport.send(request: request, resultType: ReaderAPIReferenceWrapper.self) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (response, unreadEntries)):
|
||||
guard let itemRefs = unreadEntries?.itemRefs else {
|
||||
completion(.success([]))
|
||||
return
|
||||
}
|
||||
|
||||
let itemIds = itemRefs.map { $0.itemId }
|
||||
self.storeConditionalGet(key: ConditionalGetKeys.starredEntries, headers: response.allHeaderFields)
|
||||
completion(.success(itemIds))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension ReaderAPICaller {
|
||||
|
||||
func storeConditionalGet(key: String, headers: [AnyHashable : Any]) {
|
||||
if var conditionalGet = accountMetadata?.conditionalGetInfo {
|
||||
conditionalGet[key] = HTTPConditionalGetInfo(headers: headers)
|
||||
accountMetadata?.conditionalGetInfo = conditionalGet
|
||||
}
|
||||
}
|
||||
|
||||
func addVariantHeaders(_ request: inout URLRequest) {
|
||||
if variant == .inoreader {
|
||||
request.addValue(SecretsManager.provider.inoreaderAppId, forHTTPHeaderField: "AppId")
|
||||
request.addValue(SecretsManager.provider.inoreaderAppKey, forHTTPHeaderField: "AppKey")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ struct ReaderAPIReferenceWrapper: Codable {
|
||||
}
|
||||
|
||||
struct ReaderAPIReference: Codable {
|
||||
let itemId: String
|
||||
let itemId: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case itemId = "id"
|
||||
|
@ -98,7 +98,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||
private var inspectorWindowController: InspectorWindowController?
|
||||
private var crashReportWindowController: CrashReportWindowController? // For testing only
|
||||
private let log = Log()
|
||||
private let appNewsURLString = "https://nnw.ranchero.com/feed.json"
|
||||
private let appMovementMonitor = RSAppMovementMonitor()
|
||||
#if !MAC_APP_STORE && !TEST
|
||||
private var softwareUpdater: SPUUpdater!
|
||||
@ -424,7 +423,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||
}
|
||||
|
||||
if item.action == #selector(addAppNews(_:)) {
|
||||
return !isDisplayingSheet && !AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) && !AccountManager.shared.activeAccounts.isEmpty
|
||||
return !isDisplayingSheet && !AccountManager.shared.anyAccountHasNetNewsWireNewsSubscription() && !AccountManager.shared.activeAccounts.isEmpty
|
||||
}
|
||||
|
||||
if item.action == #selector(sortByNewestArticleOnTop(_:)) || item.action == #selector(sortByOldestArticleOnTop(_:)) {
|
||||
@ -591,10 +590,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
||||
}
|
||||
|
||||
@IBAction func addAppNews(_ sender: Any?) {
|
||||
if AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) {
|
||||
if AccountManager.shared.anyAccountHasNetNewsWireNewsSubscription() {
|
||||
return
|
||||
}
|
||||
addWebFeed(appNewsURLString, name: "NetNewsWire News")
|
||||
addWebFeed(AccountManager.netNewsWireNewsURL, name: "NetNewsWire News")
|
||||
}
|
||||
|
||||
@IBAction func openWebsite(_ sender: Any?) {
|
||||
|
@ -407,22 +407,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="160" height="274"/>
|
||||
<rect key="frame" x="20" y="44" width="180" height="294"/>
|
||||
<subviews>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="PaF-du-r3c">
|
||||
<rect key="frame" x="1" y="0.0" width="158" height="273"/>
|
||||
<clipView key="contentView" id="cil-Gq-akO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="158" height="273"/>
|
||||
<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"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" viewBased="YES" id="aTp-KR-y6b">
|
||||
<rect key="frame" x="0.0" y="0.0" width="188" height="273"/>
|
||||
<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"/>
|
||||
<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="156" minWidth="40" maxWidth="1000" id="JSx-yi-vwt">
|
||||
<tableColumn width="175" 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 +435,7 @@
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="Cell" id="h2e-5a-qNO">
|
||||
<rect key="frame" x="11" y="1" width="165" height="17"/>
|
||||
<rect key="frame" x="11" y="1" width="184" 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 +447,7 @@
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="lKA-xK-bHU"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="hR2-bm-0wE">
|
||||
<rect key="frame" x="26" y="1" width="135" height="16"/>
|
||||
<rect key="frame" x="26" y="1" width="154" 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"/>
|
||||
@ -490,7 +490,7 @@
|
||||
<constraints>
|
||||
<constraint firstItem="PaF-du-r3c" firstAttribute="leading" secondItem="7UM-iq-OLB" secondAttribute="leading" constant="1" id="Brq-cg-FVo"/>
|
||||
<constraint firstItem="PaF-du-r3c" firstAttribute="top" secondItem="7UM-iq-OLB" secondAttribute="top" constant="1" id="G3u-Hk-xlH"/>
|
||||
<constraint firstAttribute="width" constant="160" id="MWF-uR-jbC"/>
|
||||
<constraint firstAttribute="width" constant="180" id="MWF-uR-jbC"/>
|
||||
<constraint firstAttribute="bottom" secondItem="PaF-du-r3c" secondAttribute="bottom" id="bjN-h8-jtK"/>
|
||||
<constraint firstAttribute="trailing" secondItem="PaF-du-r3c" secondAttribute="trailing" constant="1" id="dfm-a5-dYc"/>
|
||||
</constraints>
|
||||
@ -520,10 +520,10 @@
|
||||
</connections>
|
||||
</button>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="1gP-iQ-hAV" customClass="PreferencesControlsBackgroundView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="83" y="20" width="97" height="24"/>
|
||||
<rect key="frame" x="83" y="20" width="117" height="24"/>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Y7D-xQ-wep">
|
||||
<rect key="frame" x="188" y="20" width="242" height="298"/>
|
||||
<rect key="frame" x="208" y="20" width="222" height="318"/>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
@ -578,22 +578,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="160" height="267"/>
|
||||
<rect key="frame" x="20" y="44" width="180" height="287"/>
|
||||
<subviews>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="26" horizontalPageScroll="10" verticalLineScroll="26" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="29T-r2-ckC">
|
||||
<rect key="frame" x="1" y="0.0" width="158" height="266"/>
|
||||
<clipView key="contentView" id="dXw-GY-TP8">
|
||||
<rect key="frame" x="0.0" y="0.0" width="158" height="266"/>
|
||||
<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"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="24" viewBased="YES" id="dfn-Vn-oDp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="188" height="266"/>
|
||||
<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"/>
|
||||
<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="156" minWidth="40" maxWidth="1000" id="jBM-96-TEB">
|
||||
<tableColumn width="175" 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 +606,7 @@
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="Cell" id="xQs-6E-Kpy">
|
||||
<rect key="frame" x="11" y="1" width="165" height="17"/>
|
||||
<rect key="frame" x="11" y="1" width="184" 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 +618,7 @@
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSActionTemplate" id="OVD-Jo-TXU"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6cr-cB-qAN">
|
||||
<rect key="frame" x="26" y="1" width="135" height="16"/>
|
||||
<rect key="frame" x="26" y="1" width="154" 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"/>
|
||||
@ -655,7 +655,7 @@
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="160" id="0gU-oR-pQf"/>
|
||||
<constraint firstAttribute="width" constant="180" id="0gU-oR-pQf"/>
|
||||
<constraint firstAttribute="bottom" secondItem="29T-r2-ckC" secondAttribute="bottom" id="BMY-9E-vH2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="29T-r2-ckC" secondAttribute="trailing" constant="1" id="dAW-1i-3iD"/>
|
||||
<constraint firstItem="29T-r2-ckC" firstAttribute="top" secondItem="pjs-G4-byk" secondAttribute="top" constant="1" id="tAi-6L-Tjj"/>
|
||||
@ -687,10 +687,10 @@
|
||||
</connections>
|
||||
</button>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="sak-nS-Xfu" customClass="PreferencesControlsBackgroundView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="83" y="20" width="97" height="24"/>
|
||||
<rect key="frame" x="83" y="20" width="117" height="24"/>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="N1N-pE-gBL">
|
||||
<rect key="frame" x="188" y="20" width="242" height="291"/>
|
||||
<rect key="frame" x="208" y="20" width="222" height="311"/>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
|
@ -1,67 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097.3"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="AccountsAddViewController" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="ExtensionPointAddViewController" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="tableView" destination="YWY-HH-lRy" id="BaW-PN-aD4"/>
|
||||
<outlet property="view" destination="c22-O7-iKe" id="wFV-1Z-hFh"/>
|
||||
<outlet property="tableView" destination="lyM-Zu-Let" id="JDz-05-OOg"/>
|
||||
<outlet property="view" destination="c22-O7-iKe" id="Vfr-rK-EHC"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView misplaced="YES" id="c22-O7-iKe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="292"/>
|
||||
<customView id="c22-O7-iKe">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="aV5-XD-qtI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="292"/>
|
||||
<clipView key="contentView" id="UDd-jz-Pwe">
|
||||
<rect key="frame" x="1" y="1" width="478" height="290"/>
|
||||
<scrollView autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="y2z-6c-TH0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<clipView key="contentView" id="qCn-Bf-ICO">
|
||||
<rect key="frame" x="1" y="1" width="478" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="40" rowSizeStyle="automatic" viewBased="YES" id="YWY-HH-lRy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="478" height="290"/>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnSelection="YES" columnResizing="NO" multipleSelection="NO" autosaveColumns="NO" rowHeight="40" rowSizeStyle="automatic" viewBased="YES" id="lyM-Zu-Let">
|
||||
<rect key="frame" x="0.0" y="0.0" width="478" height="270"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="475" minWidth="40" maxWidth="1000" id="aZS-IU-bl6">
|
||||
<tableColumn width="466" minWidth="40" maxWidth="1000" id="SlU-lH-CzT">
|
||||
<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"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="lLv-gy-cGn">
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="Nhn-I6-76l">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="Cell" id="wVK-qI-WAx" customClass="AccountsAddTableCellView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="1" y="1" width="475" height="40"/>
|
||||
<tableCellView identifier="Cell" id="EGi-CQ-lPc" customClass="AccountsAddTableCellView" customModule="NetNewsWire" customModuleProvider="target">
|
||||
<rect key="frame" x="11" y="1" width="475" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="17" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xsr-pQ-ts2">
|
||||
<rect key="frame" x="20" y="8" width="174" height="24"/>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="17" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iCD-Yx-4V5">
|
||||
<rect key="frame" x="20" y="8" width="133" height="24"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="cjk-vg-Vn6">
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KmN-Zk-TBU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="24" id="EcV-jK-0Zb"/>
|
||||
<constraint firstAttribute="height" constant="24" id="hGY-vc-lAm"/>
|
||||
<constraint firstAttribute="height" constant="24" id="dbz-aC-h0q"/>
|
||||
<constraint firstAttribute="width" constant="24" id="jN0-Et-ysS"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="ros-80-3xn"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="oGL-yl-27S"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="5ut-F4-bRA">
|
||||
<rect key="frame" x="39" y="0.0" width="137" height="24"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="2mk-0x-ly6">
|
||||
<font key="font" metaFont="system" size="20"/>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="uyu-5W-IaW">
|
||||
<rect key="frame" x="39" y="4" width="96" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="iOW-VJ-bkx">
|
||||
<font key="font" textStyle="body" name=".SFNS-Regular"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
@ -76,28 +78,27 @@
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="mDB-Dl-30S">
|
||||
<button id="y48-E2-CL3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="475" height="40"/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="yTN-9d-fp3">
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="yf7-Ye-Pcd">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<font key="font" textStyle="body" name=".SFNS-Regular"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="pressed:" target="wVK-qI-WAx" id="fXc-TU-jxw"/>
|
||||
<action selector="pressed:" target="EGi-CQ-lPc" id="2a9-Bp-K3K"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="mDB-Dl-30S" firstAttribute="top" secondItem="wVK-qI-WAx" secondAttribute="top" id="5Q5-w5-FHX"/>
|
||||
<constraint firstAttribute="trailing" secondItem="mDB-Dl-30S" secondAttribute="trailing" id="ZXk-x2-YMq"/>
|
||||
<constraint firstItem="mDB-Dl-30S" firstAttribute="leading" secondItem="wVK-qI-WAx" secondAttribute="leading" id="hBc-Ju-z9p"/>
|
||||
<constraint firstItem="xsr-pQ-ts2" firstAttribute="centerY" secondItem="wVK-qI-WAx" secondAttribute="centerY" id="lQw-mm-Vnb"/>
|
||||
<constraint firstItem="xsr-pQ-ts2" firstAttribute="leading" secondItem="wVK-qI-WAx" secondAttribute="leading" constant="20" id="msT-9I-cEP"/>
|
||||
<constraint firstAttribute="bottom" secondItem="mDB-Dl-30S" secondAttribute="bottom" id="vfZ-QH-wrN"/>
|
||||
<constraint firstItem="iCD-Yx-4V5" firstAttribute="centerY" secondItem="EGi-CQ-lPc" secondAttribute="centerY" id="IS1-7W-BWY"/>
|
||||
<constraint firstItem="iCD-Yx-4V5" firstAttribute="leading" secondItem="EGi-CQ-lPc" secondAttribute="leading" constant="20" id="IsY-WH-f93"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="accountImageView" destination="cjk-vg-Vn6" id="laA-LX-gYz"/>
|
||||
<outlet property="accountNameLabel" destination="5ut-F4-bRA" id="jiR-YI-QBk"/>
|
||||
<outlet property="accountImageView" destination="KmN-Zk-TBU" id="ksF-ga-V5P"/>
|
||||
<outlet property="accountNameLabel" destination="uyu-5W-IaW" id="reb-QA-Xpx"/>
|
||||
<outlet property="imageView" destination="KmN-Zk-TBU" id="Tfy-Eb-Isb"/>
|
||||
<outlet property="titleLabel" destination="uyu-5W-IaW" id="QAe-Gk-Eeo"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
@ -106,22 +107,16 @@
|
||||
</tableView>
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="V4D-fs-sIt">
|
||||
<rect key="frame" x="1" y="119" width="223" height="15"/>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="qOf-Dj-ubR">
|
||||
<rect key="frame" x="1" y="255" width="478" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="a6y-yE-P0S">
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="XFQ-Xy-wny">
|
||||
<rect key="frame" x="224" y="17" width="15" height="102"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="aV5-XD-qtI" secondAttribute="bottom" id="OR8-mp-jGq"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aV5-XD-qtI" secondAttribute="trailing" id="djx-Hq-MmE"/>
|
||||
<constraint firstItem="aV5-XD-qtI" firstAttribute="leading" secondItem="c22-O7-iKe" secondAttribute="leading" id="hrT-XK-tRk"/>
|
||||
<constraint firstItem="aV5-XD-qtI" firstAttribute="top" secondItem="c22-O7-iKe" secondAttribute="top" id="oGe-X8-oCz"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="139" y="154"/>
|
||||
</customView>
|
||||
</objects>
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15505"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -17,13 +18,13 @@
|
||||
<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="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="398" height="162"/>
|
||||
<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"/>
|
||||
<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="108" width="144" height="38"/>
|
||||
<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"/>
|
||||
@ -31,10 +32,10 @@
|
||||
<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="proportionallyDown" image="accountCloudKit" id="9RZ-J3-ioX"/>
|
||||
<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="93" height="38"/>
|
||||
<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"/>
|
||||
@ -52,7 +53,7 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xqo-gP-MPl">
|
||||
<rect key="frame" x="303" y="13" width="81" height="32"/>
|
||||
<rect key="frame" x="310" y="13" width="75" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Create" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="oih-6c-KbS">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -65,7 +66,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9eG-vV-s8c">
|
||||
<rect key="frame" x="222" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="237" y="13" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="iVd-bO-4LN">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -78,7 +79,7 @@ Gw
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Kj-Cl-FJQ">
|
||||
<rect key="frame" x="47" y="57" width="304" height="16"/>
|
||||
<rect key="frame" x="47" y="56" width="304" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="300" id="xiK-wa-r3v"/>
|
||||
</constraints>
|
||||
@ -108,6 +109,6 @@ Gw
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="accountCloudKit" width="100" height="70"/>
|
||||
<image name="accountCloudKit" width="191" height="134"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -19,13 +19,13 @@
|
||||
<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="screenRect" x="0.0" y="0.0" width="2560" height="1417"/>
|
||||
<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"/>
|
||||
<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="161" width="213" height="38"/>
|
||||
<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"/>
|
||||
@ -33,7 +33,7 @@
|
||||
<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="proportionallyDown" image="accountLocal" id="9RZ-J3-ioX"/>
|
||||
<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"/>
|
||||
@ -54,7 +54,7 @@
|
||||
</customSpacing>
|
||||
</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="105" width="188" height="21"/>
|
||||
<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"/>
|
||||
@ -86,7 +86,7 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xqo-gP-MPl">
|
||||
<rect key="frame" x="303" y="13" width="81" height="32"/>
|
||||
<rect key="frame" x="310" y="13" width="75" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Create" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="oih-6c-KbS">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -99,7 +99,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9eG-vV-s8c">
|
||||
<rect key="frame" x="222" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="237" y="13" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="iVd-bO-4LN">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -112,7 +112,7 @@ Gw
|
||||
</connections>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3Kj-Cl-FJQ">
|
||||
<rect key="frame" x="87" y="57" width="224" height="32"/>
|
||||
<rect key="frame" x="87" y="56" width="224" height="32"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="220" id="xiK-wa-r3v"/>
|
||||
</constraints>
|
||||
@ -144,6 +144,6 @@ Gw
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="accountLocal" width="78" height="98"/>
|
||||
<image name="accountLocal" width="119" height="102"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -17,9 +17,9 @@ class AccountsAddViewController: NSViewController {
|
||||
private var accountsAddWindowController: NSWindowController?
|
||||
|
||||
#if DEBUG
|
||||
private var addableAccountTypes: [AccountType] = [.onMyMac, .cloudKit, .feedbin, .feedly, .inoreader, .newsBlur, .feedWrangler, .bazQux, .theOldReader, .freshRSS]
|
||||
private var addableAccountTypes: [AccountType] = [.onMyMac, .cloudKit, .bazQux, .feedbin, .feedly, .feedWrangler, .inoreader, .newsBlur, .theOldReader, .freshRSS]
|
||||
#else
|
||||
private var addableAccountTypes: [AccountType] = [.onMyMac, .cloudKit, .feedbin, .feedly, .inoreader, .newsBlur, .feedWrangler, .bazQux, .theOldReader, .freshRSS]
|
||||
private var addableAccountTypes: [AccountType] = [.onMyMac, .cloudKit, .bazQux, .feedbin, .feedly, .feedWrangler, .inoreader, .newsBlur, .theOldReader, .freshRSS]
|
||||
#endif
|
||||
|
||||
init() {
|
||||
@ -171,7 +171,6 @@ extension AccountsAddViewController: AccountsAddTableCellViewDelegate {
|
||||
accountsReaderAPIWindowController.accountType = .theOldReader
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsAddWindowController = accountsReaderAPIWindowController
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -249,10 +248,11 @@ private extension AccountsAddViewController {
|
||||
removeAccountType(.cloudKit)
|
||||
removeAccountType(.feedly)
|
||||
removeAccountType(.feedWrangler)
|
||||
removeAccountType(.inoreader)
|
||||
return
|
||||
}
|
||||
|
||||
if AccountManager.shared.activeAccounts.firstIndex(where: { $0.type == .cloudKit }) != nil {
|
||||
if AccountManager.shared.accounts.firstIndex(where: { $0.type == .cloudKit }) != nil {
|
||||
removeAccountType(.cloudKit)
|
||||
}
|
||||
}
|
||||
|
@ -72,18 +72,23 @@ final class AccountsDetailViewController: NSViewController, NSTextFieldDelegate
|
||||
accountsFeedbinWindowController.account = account
|
||||
accountsFeedbinWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsWindowController = accountsFeedbinWindowController
|
||||
case .freshRSS:
|
||||
let accountsFreshRSSWindowController = AccountsReaderAPIWindowController()
|
||||
accountsFreshRSSWindowController.accountType = account.type
|
||||
accountsFreshRSSWindowController.account = account
|
||||
accountsFreshRSSWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsWindowController = accountsFreshRSSWindowController
|
||||
case .inoreader, .bazQux, .theOldReader, .freshRSS:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = account.type
|
||||
accountsReaderAPIWindowController.account = account
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsWindowController = accountsReaderAPIWindowController
|
||||
break
|
||||
case .feedWrangler:
|
||||
let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController()
|
||||
accountsFeedWranglerWindowController.account = account
|
||||
accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsWindowController = accountsFeedWranglerWindowController
|
||||
case .newsBlur:
|
||||
let accountsNewsBlurWindowController = AccountsNewsBlurWindowController()
|
||||
accountsNewsBlurWindowController.account = account
|
||||
accountsNewsBlurWindowController.runSheetOnWindow(self.view.window!)
|
||||
accountsWindowController = accountsNewsBlurWindowController
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="AccountsFeedWranglerWindowController" customModuleProvider="target">
|
||||
<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"/>
|
||||
@ -21,13 +22,13 @@
|
||||
<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="433" height="249"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3840" height="2137"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
|
||||
<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="91" y="190" width="251" height="39"/>
|
||||
<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"/>
|
||||
@ -35,10 +36,10 @@
|
||||
<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="proportionallyDown" image="accountFeedWrangler" id="y38-YL-woC"/>
|
||||
<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="200" height="39"/>
|
||||
<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"/>
|
||||
@ -56,7 +57,7 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="12" columnSpacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
|
||||
<rect key="frame" x="79" y="61" width="276" height="97"/>
|
||||
<rect key="frame" x="79" y="60" width="276" height="99"/>
|
||||
<rows>
|
||||
<gridRow id="DRl-lC-vUc"/>
|
||||
<gridRow id="eW8-uH-txq"/>
|
||||
@ -69,7 +70,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="78" width="41" height="17"/>
|
||||
<rect key="frame" x="23" y="81" 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"/>
|
||||
@ -79,7 +80,7 @@
|
||||
</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="75" width="200" height="22"/>
|
||||
<rect key="frame" x="76" y="78" width="200" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="Qin-jm-4zt"/>
|
||||
</constraints>
|
||||
@ -92,7 +93,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="44" width="66" height="17"/>
|
||||
<rect key="frame" x="-2" y="48" 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"/>
|
||||
@ -102,7 +103,7 @@
|
||||
</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="41" width="200" height="22"/>
|
||||
<rect key="frame" x="76" y="45" width="200" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="eal-wa-1nU"/>
|
||||
</constraints>
|
||||
@ -118,7 +119,7 @@
|
||||
</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="6" width="104" height="17"/>
|
||||
<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"/>
|
||||
@ -130,7 +131,7 @@
|
||||
</gridCells>
|
||||
</gridView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9mz-D9-krh">
|
||||
<rect key="frame" x="340" y="13" width="79" height="32"/>
|
||||
<rect key="frame" x="347" y="13" width="73" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Action" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IMO-YT-k9Z">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -143,7 +144,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XAM-Hb-0Hw">
|
||||
<rect key="frame" x="258" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="273" y="13" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ufs-ar-BAY">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -156,7 +157,7 @@ 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="166" width="16" height="16"/>
|
||||
<rect key="frame" x="209" y="167" width="16" height="16"/>
|
||||
</progressIndicator>
|
||||
</subviews>
|
||||
<constraints>
|
||||
@ -180,6 +181,6 @@ Gw
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="accountFeedWrangler" width="144" height="144"/>
|
||||
<image name="accountFeedWrangler" width="261" height="261"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -56,7 +56,7 @@ class AccountsFeedWranglerWindowController: NSWindowController {
|
||||
return
|
||||
}
|
||||
|
||||
guard !AccountManager.shared.duplicateServiceAccount(type: .feedWrangler, username: usernameTextField.stringValue) else {
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedWrangler, username: usernameTextField.stringValue) else {
|
||||
self.errorMessageLabel.stringValue = NSLocalizedString("There is already a FeedWrangler account with that username created.", comment: "Duplicate Error")
|
||||
return
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -21,13 +22,13 @@
|
||||
<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="433" height="249"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
|
||||
<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="134" y="190" width="166" height="39"/>
|
||||
<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"/>
|
||||
@ -35,10 +36,10 @@
|
||||
<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="proportionallyDown" image="accountFeedbin" id="y38-YL-woC"/>
|
||||
<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="39"/>
|
||||
<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"/>
|
||||
@ -56,7 +57,7 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="12" columnSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
|
||||
<rect key="frame" x="82" y="61" width="270" height="97"/>
|
||||
<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>
|
||||
@ -72,7 +73,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="78" width="41" height="17"/>
|
||||
<rect key="frame" x="23" y="81" 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"/>
|
||||
@ -82,7 +83,7 @@
|
||||
</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="75" width="200" height="22"/>
|
||||
<rect key="frame" x="70" y="78" width="200" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="Qin-jm-4zt"/>
|
||||
</constraints>
|
||||
@ -95,7 +96,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="44" width="66" height="17"/>
|
||||
<rect key="frame" x="-2" y="48" 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"/>
|
||||
@ -105,7 +106,7 @@
|
||||
</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="41" width="200" height="22"/>
|
||||
<rect key="frame" x="70" y="45" width="200" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="eal-wa-1nU"/>
|
||||
</constraints>
|
||||
@ -122,7 +123,7 @@
|
||||
<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="17"/>
|
||||
<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"/>
|
||||
@ -133,7 +134,7 @@
|
||||
</gridCells>
|
||||
</gridView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9mz-D9-krh">
|
||||
<rect key="frame" x="340" y="13" width="79" height="32"/>
|
||||
<rect key="frame" x="347" y="13" width="73" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Action" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IMO-YT-k9Z">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -146,7 +147,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XAM-Hb-0Hw">
|
||||
<rect key="frame" x="258" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="273" y="13" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ufs-ar-BAY">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -159,7 +160,7 @@ 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="166" width="16" height="16"/>
|
||||
<rect key="frame" x="209" y="167" width="16" height="16"/>
|
||||
</progressIndicator>
|
||||
</subviews>
|
||||
<constraints>
|
||||
@ -183,6 +184,6 @@ Gw
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="accountFeedbin" width="120" height="102"/>
|
||||
<image name="accountFeedbin" width="369" height="343"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15702" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15702"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -21,7 +22,7 @@
|
||||
<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="433" height="249"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
|
||||
<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"/>
|
||||
@ -35,7 +36,7 @@
|
||||
<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="proportionallyDown" image="accountNewsBlur" id="y38-YL-woC"/>
|
||||
<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"/>
|
||||
@ -56,7 +57,7 @@
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<gridView xPlacement="trailing" yPlacement="center" rowAlignment="none" rowSpacing="12" columnSpacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="zBB-JH-huI">
|
||||
<rect key="frame" x="51" y="61" width="332" height="98"/>
|
||||
<rect key="frame" x="51" y="60" width="332" height="99"/>
|
||||
<rows>
|
||||
<gridRow id="DRl-lC-vUc"/>
|
||||
<gridRow id="eW8-uH-txq"/>
|
||||
@ -69,7 +70,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="80" width="122" height="16"/>
|
||||
<rect key="frame" x="-2" y="81" 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"/>
|
||||
@ -79,7 +80,7 @@
|
||||
</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="77" width="200" height="21"/>
|
||||
<rect key="frame" x="132" y="78" width="200" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="Qin-jm-4zt"/>
|
||||
</constraints>
|
||||
@ -92,7 +93,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="47" width="66" height="16"/>
|
||||
<rect key="frame" x="54" y="48" 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"/>
|
||||
@ -102,7 +103,7 @@
|
||||
</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="44" width="200" height="21"/>
|
||||
<rect key="frame" x="132" y="45" width="200" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="eal-wa-1nU"/>
|
||||
</constraints>
|
||||
@ -118,7 +119,7 @@
|
||||
</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="8" width="104" height="16"/>
|
||||
<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"/>
|
||||
@ -130,7 +131,7 @@
|
||||
</gridCells>
|
||||
</gridView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9mz-D9-krh">
|
||||
<rect key="frame" x="340" y="13" width="79" height="32"/>
|
||||
<rect key="frame" x="347" y="13" width="73" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Action" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="IMO-YT-k9Z">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -143,7 +144,7 @@ DQ
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="XAM-Hb-0Hw">
|
||||
<rect key="frame" x="258" y="13" width="82" height="32"/>
|
||||
<rect key="frame" x="273" y="13" width="76" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ufs-ar-BAY">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -180,6 +181,6 @@ Gw
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="accountNewsBlur" width="512" height="512"/>
|
||||
<image name="accountNewsBlur" width="25" height="25"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
@ -56,7 +56,7 @@ class AccountsNewsBlurWindowController: NSWindowController {
|
||||
return
|
||||
}
|
||||
|
||||
guard !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: usernameTextField.stringValue) else {
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: usernameTextField.stringValue) else {
|
||||
self.errorMessageLabel.stringValue = NSLocalizedString("There is already a NewsBlur account with that username created.", comment: "Duplicate Error")
|
||||
return
|
||||
}
|
||||
|
@ -8,12 +8,17 @@
|
||||
|
||||
import AppKit
|
||||
import Account
|
||||
import SwiftUI
|
||||
import RSCore
|
||||
|
||||
|
||||
final class AccountsPreferencesViewController: NSViewController {
|
||||
|
||||
@IBOutlet weak var tableView: NSTableView!
|
||||
@IBOutlet weak var detailView: NSView!
|
||||
@IBOutlet weak var deleteButton: NSButton!
|
||||
var addAccountDelegate: AccountsPreferencesAddAccountDelegate?
|
||||
|
||||
|
||||
private var sortedAccounts = [Account]()
|
||||
|
||||
@ -23,12 +28,12 @@ final class AccountsPreferencesViewController: NSViewController {
|
||||
updateSortedAccounts()
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
addAccountDelegate = self
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidAddAccount, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange(_:)), name: .UserDidDeleteAccount, object: nil)
|
||||
|
||||
showController(AccountsAddViewController())
|
||||
|
||||
// Fix tableView frame — for some reason IB wants it 1pt wider than the clip view. This leads to unwanted horizontal scrolling.
|
||||
var rTable = tableView.frame
|
||||
@ -37,8 +42,9 @@ final class AccountsPreferencesViewController: NSViewController {
|
||||
}
|
||||
|
||||
@IBAction func addAccount(_ sender: Any) {
|
||||
tableView.selectRowIndexes([], byExtendingSelection: false)
|
||||
showController(AccountsAddViewController())
|
||||
let controller = NSHostingController(rootView: AddAccountsView(delegate: self))
|
||||
controller.rootView.parent = controller
|
||||
presentAsSheet(controller)
|
||||
}
|
||||
|
||||
@IBAction func removeAccount(_ sender: Any) {
|
||||
@ -62,7 +68,6 @@ final class AccountsPreferencesViewController: NSViewController {
|
||||
if result == NSApplication.ModalResponse.alertFirstButtonReturn {
|
||||
guard let self = self else { return }
|
||||
AccountManager.shared.deleteAccount(self.sortedAccounts[self.tableView.selectedRow])
|
||||
self.showController(AccountsAddViewController())
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,6 +137,88 @@ extension AccountsPreferencesViewController: NSTableViewDelegate {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - AccountsPreferencesAddAccountDelegate
|
||||
protocol AccountsPreferencesAddAccountDelegate {
|
||||
func presentSheetForAccount(_ accountType: AccountType)
|
||||
}
|
||||
|
||||
extension AccountsPreferencesViewController: AccountsPreferencesAddAccountDelegate {
|
||||
func presentSheetForAccount(_ accountType: AccountType) {
|
||||
switch accountType {
|
||||
case .onMyMac:
|
||||
let accountsAddLocalWindowController = AccountsAddLocalWindowController()
|
||||
accountsAddLocalWindowController.runSheetOnWindow(self.view.window!)
|
||||
|
||||
case .cloudKit:
|
||||
let accountsAddCloudKitWindowController = AccountsAddCloudKitWindowController()
|
||||
accountsAddCloudKitWindowController.runSheetOnWindow(self.view.window!) { response in
|
||||
if response == NSApplication.ModalResponse.OK {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
|
||||
case .feedbin:
|
||||
let accountsFeedbinWindowController = AccountsFeedbinWindowController()
|
||||
accountsFeedbinWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .feedWrangler:
|
||||
let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController()
|
||||
accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .freshRSS:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .freshRSS
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .feedly:
|
||||
let addAccount = OAuthAccountAuthorizationOperation(accountType: .feedly)
|
||||
addAccount.delegate = self
|
||||
addAccount.presentationAnchor = self.view.window!
|
||||
runAwaitingFeedlyLoginAlertModal(forLifetimeOf: addAccount)
|
||||
MainThreadOperationQueue.shared.add(addAccount)
|
||||
case .newsBlur:
|
||||
let accountsNewsBlurWindowController = AccountsNewsBlurWindowController()
|
||||
accountsNewsBlurWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .inoreader:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .inoreader
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .bazQux:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .bazQux
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
case .theOldReader:
|
||||
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
|
||||
accountsReaderAPIWindowController.accountType = .theOldReader
|
||||
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
|
||||
}
|
||||
}
|
||||
|
||||
private func runAwaitingFeedlyLoginAlertModal(forLifetimeOf operation: OAuthAccountAuthorizationOperation) {
|
||||
let alert = NSAlert()
|
||||
alert.alertStyle = .informational
|
||||
alert.messageText = NSLocalizedString("Waiting for access to Feedly",
|
||||
comment: "Alert title when adding a Feedly account and waiting for authorization from the user.")
|
||||
|
||||
alert.informativeText = NSLocalizedString("Your default web browser will open the Feedly login for you to authorize access.",
|
||||
comment: "Alert informative text when adding a Feedly account and waiting for authorization from the user.")
|
||||
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel"))
|
||||
|
||||
let attachedWindow = self.view.window!
|
||||
|
||||
alert.beginSheetModal(for: attachedWindow) { response in
|
||||
if response == .alertFirstButtonReturn {
|
||||
operation.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
operation.completionBlock = { _ in
|
||||
guard alert.window.isVisible else {
|
||||
return
|
||||
}
|
||||
attachedWindow.endSheet(alert.window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private extension AccountsPreferencesViewController {
|
||||
@ -155,3 +242,31 @@ private extension AccountsPreferencesViewController {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension AccountsPreferencesViewController: OAuthAccountAuthorizationOperationDelegate {
|
||||
|
||||
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) {
|
||||
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
|
||||
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
|
||||
// When this authorization is granted, the browser remains the foreground app which is unfortunate
|
||||
// because the user probably wants to see the result of authorizing NetNewsWire to act on their behalf.
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
account.refreshAll { [weak self] result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
self?.presentError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) {
|
||||
// `OAuthAccountAuthorizationOperation` is using `ASWebAuthenticationSession` which bounces the user
|
||||
// to their browser on macOS for authorizing NetNewsWire to access the user's Feedly account.
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
view.window?.presentError(error)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17505"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17506"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -27,7 +27,7 @@
|
||||
<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="433" height="249"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1415"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1792" height="1095"/>
|
||||
<view key="contentView" misplaced="YES" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="433" height="249"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@ -41,7 +41,7 @@
|
||||
<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="proportionallyDown" id="y38-YL-woC"/>
|
||||
<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"/>
|
||||
|
@ -91,7 +91,7 @@ class AccountsReaderAPIWindowController: NSWindowController {
|
||||
return
|
||||
}
|
||||
|
||||
guard !AccountManager.shared.duplicateServiceAccount(type: accountType, username: usernameTextField.stringValue) else {
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: accountType, username: usernameTextField.stringValue) else {
|
||||
self.errorMessageLabel.stringValue = NSLocalizedString("There is already an account of this type with that username created.", comment: "Duplicate Error")
|
||||
return
|
||||
}
|
||||
|
265
Mac/Preferences/Accounts/AddAccountsView.swift
Normal file
@ -0,0 +1,265 @@
|
||||
//
|
||||
// AddAccountsView.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Stuart Breckenridge on 28/10/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Account
|
||||
|
||||
private enum AddAccountSections: Int, CaseIterable {
|
||||
case local = 0
|
||||
case icloud
|
||||
case web
|
||||
case selfhosted
|
||||
|
||||
var sectionHeader: String {
|
||||
switch self {
|
||||
case .local:
|
||||
return NSLocalizedString("Local", comment: "Local Account")
|
||||
case .icloud:
|
||||
return NSLocalizedString("iCloud", comment: "iCloud Account")
|
||||
case .web:
|
||||
return NSLocalizedString("Web", comment: "Web Account")
|
||||
case .selfhosted:
|
||||
return NSLocalizedString("Self-hosted", comment: "Self hosted Account")
|
||||
}
|
||||
}
|
||||
|
||||
var sectionFooter: String {
|
||||
switch self {
|
||||
case .local:
|
||||
return NSLocalizedString("This account does not sync subscriptions across devices.", comment: "Local Account")
|
||||
case .icloud:
|
||||
return NSLocalizedString("Use your iCloud account to sync your subscriptions across your iOS and macOS devices.", comment: "iCloud Account")
|
||||
case .web:
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
var sectionContent: [AccountType] {
|
||||
switch self {
|
||||
case .local:
|
||||
return [.onMyMac]
|
||||
case .icloud:
|
||||
return [.cloudKit]
|
||||
case .web:
|
||||
return [.bazQux, .feedbin, .feedly, .feedWrangler, .inoreader, .newsBlur, .theOldReader]
|
||||
case .selfhosted:
|
||||
return [.freshRSS]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddAccountsView: View {
|
||||
|
||||
weak var parent: NSHostingController<AddAccountsView>? // required because presentationMode.dismiss() doesn't work
|
||||
var addAccountDelegate: AccountsPreferencesAddAccountDelegate?
|
||||
@State private var selectedAccount: AccountType = .onMyMac
|
||||
|
||||
init(delegate: AccountsPreferencesAddAccountDelegate?) {
|
||||
self.addAccountDelegate = delegate
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Choose an account type to add...")
|
||||
.font(.headline)
|
||||
.padding()
|
||||
|
||||
localAccount
|
||||
icloudAccount
|
||||
webAccounts
|
||||
selfhostedAccounts
|
||||
|
||||
HStack(spacing: 12) {
|
||||
Spacer()
|
||||
if #available(OSX 11.0, *) {
|
||||
Button(action: {
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Cancel")
|
||||
.frame(width: 80)
|
||||
})
|
||||
.help("Cancel")
|
||||
.keyboardShortcut(.cancelAction)
|
||||
|
||||
} else {
|
||||
Button(action: {
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Cancel")
|
||||
.frame(width: 80)
|
||||
})
|
||||
.accessibility(label: Text("Add Account"))
|
||||
}
|
||||
if #available(OSX 11.0, *) {
|
||||
Button(action: {
|
||||
addAccountDelegate?.presentSheetForAccount(selectedAccount)
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Continue")
|
||||
.frame(width: 80)
|
||||
})
|
||||
.help("Add Account")
|
||||
.keyboardShortcut(.defaultAction)
|
||||
|
||||
} else {
|
||||
Button(action: {
|
||||
addAccountDelegate?.presentSheetForAccount(selectedAccount)
|
||||
parent?.dismiss(nil)
|
||||
}, label: {
|
||||
Text("Continue")
|
||||
.frame(width: 80)
|
||||
})
|
||||
}
|
||||
}.padding(.vertical, 8)
|
||||
}
|
||||
.pickerStyle(RadioGroupPickerStyle())
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.frame(width: 420)
|
||||
.padding()
|
||||
}
|
||||
|
||||
var localAccount: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Local")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.local.sectionContent, id: \.self, content: { account in
|
||||
HStack(alignment: .top) {
|
||||
account.image()
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.offset(CGSize(width: 0, height: -2.5))
|
||||
.padding(.leading, 4)
|
||||
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(account.localizedAccountName())
|
||||
Text(AddAccountSections.local.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.tag(account)
|
||||
})
|
||||
})
|
||||
.pickerStyle(RadioGroupPickerStyle())
|
||||
.offset(x: 7.5, y: 0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var icloudAccount: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("iCloud")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.icloud.sectionContent, id: \.self, content: { account in
|
||||
HStack(alignment: .top) {
|
||||
account.image()
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.offset(CGSize(width: 0, height: -5))
|
||||
.padding(.leading, 4)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(account.localizedAccountName())
|
||||
Text(AddAccountSections.icloud.sectionFooter).foregroundColor(.gray)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
.tag(account)
|
||||
})
|
||||
})
|
||||
.offset(x: 7.5, y: 0)
|
||||
.disabled(isCloudInUse())
|
||||
}
|
||||
}
|
||||
|
||||
var webAccounts: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Web")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.web.sectionContent.filter({ isRestricted($0) != true }), id: \.self, content: { account in
|
||||
|
||||
HStack(alignment: .center) {
|
||||
account.image()
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.padding(.leading, 4)
|
||||
|
||||
VStack(alignment: .leading) {
|
||||
Text(account.localizedAccountName())
|
||||
}
|
||||
}
|
||||
.tag(account)
|
||||
|
||||
})
|
||||
})
|
||||
.offset(x: 7.5, y: 0)
|
||||
}
|
||||
}
|
||||
|
||||
var selfhostedAccounts: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Self-hosted")
|
||||
.font(.headline)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 8)
|
||||
Picker(selection: $selectedAccount, label: Text(""), content: {
|
||||
ForEach(AddAccountSections.selfhosted.sectionContent, id: \.self, content: { account in
|
||||
HStack(alignment: .top) {
|
||||
account.image()
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 25, height: 25, alignment: .center)
|
||||
.offset(CGSize(width: 0, height: -4))
|
||||
.padding(.leading, 4)
|
||||
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(account.localizedAccountName())
|
||||
Text("Web and self-hosted accounts sync across all signed-in devices.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}.tag(account)
|
||||
})
|
||||
})
|
||||
.offset(x: 7.5, y: 0)
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private func isCloudInUse() -> Bool {
|
||||
AccountManager.shared.accounts.contains(where: { $0.type == .cloudKit })
|
||||
}
|
||||
|
||||
private func isRestricted(_ accountType: AccountType) -> Bool {
|
||||
if AppDefaults.shared.isDeveloperBuild && (accountType == .feedly || accountType == .feedWrangler || accountType == .inoreader) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct AddAccountsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AddAccountsView(delegate: nil)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16096" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="17505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="17505"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -32,7 +33,7 @@
|
||||
<tableViewGridLines key="gridStyleMask" horizontal="YES"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn width="475" minWidth="40" maxWidth="1000" id="SlU-lH-CzT">
|
||||
<tableColumn width="466" minWidth="40" maxWidth="1000" id="SlU-lH-CzT">
|
||||
<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"/>
|
||||
@ -49,7 +50,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="horizontal" alignment="centerY" spacing="17" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iCD-Yx-4V5">
|
||||
<rect key="frame" x="20" y="8" width="173" height="24"/>
|
||||
<rect key="frame" x="20" y="8" width="174" height="24"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="KmN-Zk-TBU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||
@ -60,7 +61,7 @@
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="oGL-yl-27S"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="uyu-5W-IaW">
|
||||
<rect key="frame" x="39" y="1" width="136" height="23"/>
|
||||
<rect key="frame" x="39" y="0.0" width="137" height="24"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="iOW-VJ-bkx">
|
||||
<font key="font" metaFont="system" size="20"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -77,6 +78,17 @@
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<button id="y48-E2-CL3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="475" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="bevel" bezelStyle="rounded" alignment="center" imageScaling="proportionallyDown" inset="2" id="yf7-Ye-Pcd">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="pressed:" target="EGi-CQ-lPc" id="hgF-B0-Dyh"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="iCD-Yx-4V5" firstAttribute="centerY" secondItem="EGi-CQ-lPc" secondAttribute="centerY" id="IS1-7W-BWY"/>
|
||||
@ -94,7 +106,7 @@
|
||||
</subviews>
|
||||
</clipView>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="qOf-Dj-ubR">
|
||||
<rect key="frame" x="1" y="119" width="223" height="15"/>
|
||||
<rect key="frame" x="1" y="255" width="478" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="XFQ-Xy-wny">
|
||||
|
@ -8,9 +8,21 @@
|
||||
|
||||
import AppKit
|
||||
|
||||
protocol ExtensionPointTableCellViewDelegate: class {
|
||||
func addExtensionPoint(_ extensionPointType: ExtensionPoint.Type)
|
||||
}
|
||||
|
||||
class ExtensionPointAddTableCellView: NSTableCellView {
|
||||
|
||||
weak var delegate: ExtensionPointTableCellViewDelegate?
|
||||
var extensionPointType: ExtensionPoint.Type?
|
||||
|
||||
@IBOutlet weak var templateImageView: NSImageView?
|
||||
@IBOutlet weak var titleLabel: NSTextField?
|
||||
|
||||
@IBAction func pressed(_ sender: Any) {
|
||||
guard let extensionPointType = extensionPointType else { return }
|
||||
delegate?.addExtensionPoint(extensionPointType)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ extension ExtensionPointAddViewController: NSTableViewDataSource {
|
||||
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - NSTableViewDelegate
|
||||
@ -50,9 +51,10 @@ extension ExtensionPointAddViewController: NSTableViewDataSource {
|
||||
extension ExtensionPointAddViewController: NSTableViewDelegate {
|
||||
|
||||
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
|
||||
|
||||
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "Cell"), owner: nil) as? ExtensionPointAddTableCellView {
|
||||
let extensionPointType = availableExtensionPointTypes[row]
|
||||
cell.extensionPointType = extensionPointType
|
||||
cell.delegate = self
|
||||
cell.titleLabel?.stringValue = extensionPointType.title
|
||||
cell.imageView?.image = extensionPointType.templateImage
|
||||
return cell
|
||||
@ -60,20 +62,15 @@ extension ExtensionPointAddViewController: NSTableViewDelegate {
|
||||
return nil
|
||||
}
|
||||
|
||||
func tableViewSelectionDidChange(_ notification: Notification) {
|
||||
let selectedRow = tableView.selectedRow
|
||||
guard selectedRow != -1 else {
|
||||
return
|
||||
}
|
||||
|
||||
let extensionPointType = availableExtensionPointTypes[selectedRow]
|
||||
}
|
||||
|
||||
extension ExtensionPointAddViewController: ExtensionPointTableCellViewDelegate {
|
||||
|
||||
func addExtensionPoint(_ extensionPointType: ExtensionPoint.Type) {
|
||||
let windowController = ExtensionPointEnableWindowController()
|
||||
windowController.extensionPointType = extensionPointType
|
||||
windowController.runSheetOnWindow(self.view.window!)
|
||||
extensionPointAddWindowController = windowController
|
||||
|
||||
tableView.selectRowIndexes([], byExtendingSelection: false)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.800",
|
||||
"green" : "0.600",
|
||||
"red" : "0.200"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.761",
|
||||
"green" : "0.561",
|
||||
"red" : "0.169"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.698",
|
||||
"green" : "0.682",
|
||||
"red" : "0.682"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.400",
|
||||
"green" : "0.388",
|
||||
"red" : "0.388"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.808",
|
||||
"green" : "0.369",
|
||||
"red" : "0.027"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.753",
|
||||
"green" : "0.271",
|
||||
"red" : "0.027"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.298",
|
||||
"green" : "0.694",
|
||||
"red" : "0.169"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.220",
|
||||
"green" : "0.616",
|
||||
"red" : "0.090"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.737",
|
||||
"green" : "0.569",
|
||||
"red" : "0.118"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.698",
|
||||
"green" : "0.529",
|
||||
"red" : "0.078"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.741",
|
||||
"green" : "0.384",
|
||||
"red" : "0.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.663",
|
||||
"green" : "0.306",
|
||||
"red" : "0.004"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.780",
|
||||
"green" : "0.482",
|
||||
"red" : "0.004"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.702",
|
||||
"green" : "0.404",
|
||||
"red" : "0.004"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.478",
|
||||
"red" : "0.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.518",
|
||||
"red" : "0.039"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.584",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.039",
|
||||
"green" : "0.624",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.145",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0.000",
|
||||
"green" : "0.067",
|
||||
"red" : "0.882"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "bazqux-logo.png",
|
||||
"filename" : "bazqux-any.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
@ -11,6 +11,6 @@
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountBazQux.imageset/bazqux-any.pdf
vendored
Normal file
Before Width: | Height: | Size: 2.4 KiB |
@ -1,15 +1,26 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "icloud.pdf"
|
||||
"filename" : "icloud-any.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "icloud-dark.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountCloudKit.imageset/icloud-any.pdf
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/accountCloudKit.imageset/icloud-dark.pdf
vendored
Normal file
@ -1,15 +1,59 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "feedwranger-any-slice.png",
|
||||
"idiom" : "universal",
|
||||
"filename" : "outline-512.png"
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "feedwranger-dark-slice.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "feedwranger-any-slice@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "feedwranger-dark-slice@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "feedwranger-any-slice@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "feedwranger-dark-slice@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice.png
vendored
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@2x.png
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@3x.png
vendored
Normal file
After Width: | Height: | Size: 153 KiB |
BIN
Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice.png
vendored
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@2x.png
vendored
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@3x.png
vendored
Normal file
After Width: | Height: | Size: 153 KiB |
Before Width: | Height: | Size: 48 KiB |
@ -1,7 +1,17 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "feedbin-logo.pdf",
|
||||
"filename" : "feedbin-logo-filled.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "feedbin-logo-filled-1.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
@ -10,6 +20,7 @@
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountFeedbin.imageset/feedbin-logo-filled-1.pdf
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/accountFeedbin.imageset/feedbin-logo-filled.pdf
vendored
Normal file
@ -1,15 +1,25 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "accountFeedly.pdf"
|
||||
"filename" : "feedly-logo-any.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "feedly-logo-dark.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountFeedly.imageset/feedly-logo-any.pdf
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/accountFeedly.imageset/feedly-logo-dark.pdf
vendored
Normal file
@ -1,15 +1,16 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "accountFreshRSS.pdf"
|
||||
"filename" : "freshrss-any.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,17 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "inoreader_logo.pdf",
|
||||
"filename" : "inoreader_logo-any.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "inoreader_logo-dark.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
@ -11,6 +21,6 @@
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountInoreader.imageset/inoreader_logo-any.pdf
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/accountInoreader.imageset/inoreader_logo-dark.pdf
vendored
Normal file
@ -1,15 +1,25 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "localAccountMac.pdf"
|
||||
"filename" : "localAccountLight.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "localAccountDark-1.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "newsblur-512.png"
|
||||
"filename" : "Newsblur-any.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountNewsBlur.imageset/Newsblur-any.pdf
vendored
Normal file
Before Width: | Height: | Size: 53 KiB |
@ -1,7 +1,17 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "oldreader-icon.pdf",
|
||||
"filename" : "oldreader-icon-any.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "oldreader-icon-dark.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
@ -11,6 +21,6 @@
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true,
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/accountTheOldReader.imageset/oldreader-icon-any.pdf
vendored
Normal file
BIN
Mac/Resources/Assets.xcassets/accountTheOldReader.imageset/oldreader-icon-dark.pdf
vendored
Normal file
@ -1,8 +1,18 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "MarsEditOfficial.pdf",
|
||||
"idiom" : "universal"
|
||||
"filename" : "MarsEdit4Icon24.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "MarsEdit4Icon48.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
@ -10,6 +20,6 @@
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/extensionPointMarsEdit.imageset/MarsEdit4Icon24.png
vendored
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
Mac/Resources/Assets.xcassets/extensionPointMarsEdit.imageset/MarsEdit4Icon48.png
vendored
Normal file
After Width: | Height: | Size: 5.8 KiB |
@ -1,8 +1,18 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "micro-dot-blog.pdf",
|
||||
"idiom" : "universal"
|
||||
"filename" : "micro.blog.24.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "micro.blog.48.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
@ -10,6 +20,6 @@
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
BIN
Mac/Resources/Assets.xcassets/extensionPointMicroblog.imageset/micro.blog.24.png
vendored
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Mac/Resources/Assets.xcassets/extensionPointMicroblog.imageset/micro.blog.48.png
vendored
Normal file
After Width: | Height: | Size: 4.3 KiB |
@ -1,17 +1,7 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "reddit-light.pdf",
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"filename" : "reddit-dark.pdf",
|
||||
"filename" : "reddit_logo.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
@ -20,6 +10,6 @@
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,6 @@
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "template"
|
||||
"template-rendering-intent" : "original"
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
||||
|
||||
var appName: String!
|
||||
|
||||
private let appNewsURLString = "https://nnw.ranchero.com/feed.json"
|
||||
private let appMovementMonitor = RSAppMovementMonitor()
|
||||
#if !MAC_APP_STORE && !TEST
|
||||
var softwareUpdater: SPUUpdater!
|
||||
|
@ -23,6 +23,8 @@
|
||||
1729529524AA1CAA00D65E66 /* GeneralPreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */; };
|
||||
1729529724AA1CD000D65E66 /* MacPreferencePanes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529624AA1CD000D65E66 /* MacPreferencePanes.swift */; };
|
||||
1729529B24AA1FD200D65E66 /* MacSearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1729529A24AA1FD200D65E66 /* MacSearchField.swift */; };
|
||||
173A64172547BE0900267F6E /* AccountType+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173A64162547BE0900267F6E /* AccountType+Helpers.swift */; };
|
||||
173A642C2547BE9600267F6E /* AccountType+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 173A64162547BE0900267F6E /* AccountType+Helpers.swift */; };
|
||||
175942AA24AD533200585066 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
|
||||
175942AB24AD533200585066 /* RefreshInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE4226F4DFA0010922C /* RefreshInterval.swift */; };
|
||||
1769E32224BC5925000E1E8E /* AccountsPreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1769E32124BC5925000E1E8E /* AccountsPreferencesModel.swift */; };
|
||||
@ -39,6 +41,8 @@
|
||||
177A0C2D25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 177A0C2C25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift */; };
|
||||
17897ACA24C281A40014BA03 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17897AC924C281A40014BA03 /* InspectorView.swift */; };
|
||||
17897ACB24C281A40014BA03 /* InspectorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17897AC924C281A40014BA03 /* InspectorView.swift */; };
|
||||
178A9F9D2549449F00AB7E9D /* AddAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */; };
|
||||
178A9F9E2549449F00AB7E9D /* AddAccountsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */; };
|
||||
17930ED424AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */; };
|
||||
17930ED524AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */; };
|
||||
1799E6A924C2F93F00511E91 /* InspectorPlatformModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */; };
|
||||
@ -1453,6 +1457,7 @@
|
||||
1729529224AA1CAA00D65E66 /* GeneralPreferencesView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralPreferencesView.swift; sourceTree = "<group>"; };
|
||||
1729529624AA1CD000D65E66 /* MacPreferencePanes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacPreferencePanes.swift; sourceTree = "<group>"; };
|
||||
1729529A24AA1FD200D65E66 /* MacSearchField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacSearchField.swift; sourceTree = "<group>"; };
|
||||
173A64162547BE0900267F6E /* AccountType+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccountType+Helpers.swift"; sourceTree = "<group>"; };
|
||||
1769E32124BC5925000E1E8E /* AccountsPreferencesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsPreferencesModel.swift; sourceTree = "<group>"; };
|
||||
1769E32424BC5A65000E1E8E /* AddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountView.swift; sourceTree = "<group>"; };
|
||||
1769E32624BC5B6C000E1E8E /* AddAccountModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountModel.swift; sourceTree = "<group>"; };
|
||||
@ -1465,6 +1470,7 @@
|
||||
1776E88D24AC5F8A00E78166 /* AppDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefaults.swift; sourceTree = "<group>"; };
|
||||
177A0C2C25454AAB00D7EAF6 /* ReaderAPIAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderAPIAccountViewController.swift; sourceTree = "<group>"; };
|
||||
17897AC924C281A40014BA03 /* InspectorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorView.swift; sourceTree = "<group>"; };
|
||||
178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddAccountsView.swift; sourceTree = "<group>"; };
|
||||
17930ED324AF10EE00A9BA52 /* AddWebFeedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedView.swift; sourceTree = "<group>"; };
|
||||
1799E6A824C2F93F00511E91 /* InspectorPlatformModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorPlatformModifier.swift; sourceTree = "<group>"; };
|
||||
1799E6CC24C320D600511E91 /* InspectorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InspectorModel.swift; sourceTree = "<group>"; };
|
||||
@ -3303,6 +3309,7 @@
|
||||
849A97561ED9EB0D007D329B /* Extensions */,
|
||||
510C43F5243D0325009F70C3 /* ExtensionPoints */,
|
||||
511D43CE231FA51100FB1562 /* Resources */,
|
||||
173A64162547BE0900267F6E /* AccountType+Helpers.swift */,
|
||||
);
|
||||
path = Shared;
|
||||
sourceTree = "<group>";
|
||||
@ -3341,6 +3348,7 @@
|
||||
84C9FC6F22629E1200D921D6 /* Accounts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
178A9F9C2549449F00AB7E9D /* AddAccountsView.swift */,
|
||||
84C9FC7222629E1200D921D6 /* AccountsPreferencesViewController.swift */,
|
||||
51EF0F8D2279C9260050506E /* AccountsAdd.xib */,
|
||||
51EF0F8F2279C9500050506E /* AccountsAddViewController.swift */,
|
||||
@ -4944,6 +4952,7 @@
|
||||
515A5108243D0CCD0089E588 /* TwitterFeedProvider-Extensions.swift in Sources */,
|
||||
65ED402B235DEF6C0081F399 /* ImportOPMLWindowController.swift in Sources */,
|
||||
65ED402C235DEF6C0081F399 /* TimelineTableView.swift in Sources */,
|
||||
178A9F9E2549449F00AB7E9D /* AddAccountsView.swift in Sources */,
|
||||
65ED402D235DEF6C0081F399 /* DetailStatusBarView.swift in Sources */,
|
||||
65ED402E235DEF6C0081F399 /* MainWindowController+Scriptability.swift in Sources */,
|
||||
65ED402F235DEF6C0081F399 /* PreferencesWindowController.swift in Sources */,
|
||||
@ -5118,6 +5127,7 @@
|
||||
5108F6B72375E612001ABC45 /* CacheCleaner.swift in Sources */,
|
||||
518651DA235621840078E021 /* ImageTransition.swift in Sources */,
|
||||
51C266EA238C334800F53014 /* ContextMenuPreviewViewController.swift in Sources */,
|
||||
173A642C2547BE9600267F6E /* AccountType+Helpers.swift in Sources */,
|
||||
51627A6923861DED007B3B4B /* MasterFeedViewController+Drop.swift in Sources */,
|
||||
514219372352510100E07E2C /* ImageScrollView.swift in Sources */,
|
||||
516AE9B32371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift in Sources */,
|
||||
@ -5278,6 +5288,7 @@
|
||||
848D578E21543519005FFAD5 /* PasteboardWebFeed.swift in Sources */,
|
||||
5144EA2F2279FAB600D19003 /* AccountsDetailViewController.swift in Sources */,
|
||||
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,
|
||||
173A64172547BE0900267F6E /* AccountType+Helpers.swift in Sources */,
|
||||
518C3193237B00D9004D740F /* DetailIconSchemeHandler.swift in Sources */,
|
||||
84C9FC6722629B9000D921D6 /* AppDelegate.swift in Sources */,
|
||||
510C417F24E5D1AE008226FD /* ExtensionContainersFile.swift in Sources */,
|
||||
@ -5303,6 +5314,7 @@
|
||||
84B99C9D1FAE83C600ECDEDB /* DeleteCommand.swift in Sources */,
|
||||
849A97541ED9EAC0007D329B /* AddWebFeedWindowController.swift in Sources */,
|
||||
5144EA40227A37EC00D19003 /* ImportOPMLWindowController.swift in Sources */,
|
||||
178A9F9D2549449F00AB7E9D /* AddAccountsView.swift in Sources */,
|
||||
51C4CFF024D37D1F00AF9874 /* Secrets.swift in Sources */,
|
||||
849A976D1ED9EBC8007D329B /* TimelineTableView.swift in Sources */,
|
||||
51333D1624685D2E00EB5C91 /* AddRedditFeedWindowController.swift in Sources */,
|
||||
|
86
Shared/AccountType+Helpers.swift
Normal file
@ -0,0 +1,86 @@
|
||||
//
|
||||
// AccountType+Helpers.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Stuart Breckenridge on 27/10/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Account
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
import SwiftUI
|
||||
|
||||
extension AccountType {
|
||||
|
||||
// TODO: Move this to the Account Package.
|
||||
|
||||
func localizedAccountName() -> String {
|
||||
|
||||
switch self {
|
||||
case .onMyMac:
|
||||
let defaultName: String
|
||||
#if os(macOS)
|
||||
defaultName = NSLocalizedString("On My Mac", comment: "Account name")
|
||||
#else
|
||||
if UIDevice.current.userInterfaceIdiom == .pad {
|
||||
defaultName = NSLocalizedString("On My iPad", comment: "Account name")
|
||||
} else {
|
||||
defaultName = NSLocalizedString("On My iPhone", comment: "Account name")
|
||||
}
|
||||
#endif
|
||||
return defaultName
|
||||
case .bazQux:
|
||||
return NSLocalizedString("BazQux", comment: "Account name")
|
||||
case .cloudKit:
|
||||
return NSLocalizedString("iCloud", comment: "Account name")
|
||||
case .feedWrangler:
|
||||
return NSLocalizedString("FeedWrangler", comment: "Account name")
|
||||
case .feedbin:
|
||||
return NSLocalizedString("Feedbin", comment: "Account name")
|
||||
case .feedly:
|
||||
return NSLocalizedString("Feedly", comment: "Account name")
|
||||
case .freshRSS:
|
||||
return NSLocalizedString("FreshRSS", comment: "Account name")
|
||||
case .inoreader:
|
||||
return NSLocalizedString("Inoreader", comment: "Account name")
|
||||
case .newsBlur:
|
||||
return NSLocalizedString("NewsBlur", comment: "Account name")
|
||||
case .theOldReader:
|
||||
return NSLocalizedString("The Old Reader", comment: "Account name")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SwiftUI Images
|
||||
func image() -> Image {
|
||||
switch self {
|
||||
case .onMyMac:
|
||||
return Image("accountLocal")
|
||||
case .bazQux:
|
||||
return Image("accountBazQux")
|
||||
case .cloudKit:
|
||||
return Image("accountCloudKit")
|
||||
case .feedWrangler:
|
||||
return Image("accountFeedWrangler")
|
||||
case .feedbin:
|
||||
return Image("accountFeedbin")
|
||||
case .feedly:
|
||||
return Image("accountFeedly")
|
||||
case .freshRSS:
|
||||
return Image("accountFreshRSS")
|
||||
case .inoreader:
|
||||
return Image("accountInoreader")
|
||||
case .newsBlur:
|
||||
return Image("accountNewsBlur")
|
||||
case .theOldReader:
|
||||
return Image("accountTheOldReader")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17126"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17502"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@ -586,7 +586,7 @@
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="pNe-n6-tVf">
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="pNe-n6-tVf">
|
||||
<rect key="frame" x="20" y="61.5" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="pNe-n6-tVf" id="yQJ-L0-qVZ">
|
||||
@ -617,7 +617,7 @@
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="mCx-af-pd3">
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="mCx-af-pd3">
|
||||
<rect key="frame" x="20" y="105" width="374" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mCx-af-pd3" id="o1U-Qv-4gz">
|
||||
|
@ -80,7 +80,7 @@ class FeedWranglerAccountViewController: 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: .feedWrangler, username: trimmedEmail) else {
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedWrangler, username: trimmedEmail) else {
|
||||
showError(NSLocalizedString("There is already a FeedWrangler account with that username created.", comment: "Duplicate Error"))
|
||||
return
|
||||
}
|
||||
|
@ -80,22 +80,28 @@ class NewsBlurAccountViewController: UITableViewController {
|
||||
return
|
||||
}
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .newsBlur, username: trimmedUsername) else {
|
||||
showError(NSLocalizedString("There is already a NewsBlur account with that username created.", comment: "Duplicate Error"))
|
||||
return
|
||||
}
|
||||
|
||||
let password = passwordTextField.text ?? ""
|
||||
|
||||
startAnimatingActivityIndicator()
|
||||
disableNavigation()
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .newsBlurBasic, username: trimmedUsername, secret: password)
|
||||
Account.validateCredentials(type: .newsBlur, credentials: credentials) { result in
|
||||
let basicCredentials = Credentials(type: .newsBlurBasic, username: trimmedUsername, secret: password)
|
||||
Account.validateCredentials(type: .newsBlur, credentials: basicCredentials) { result in
|
||||
|
||||
self.stopAnimatingActivityIndicator()
|
||||
self.enableNavigation()
|
||||
|
||||
switch result {
|
||||
case .success(let credentials):
|
||||
if let credentials = credentials {
|
||||
case .success(let sessionCredentials):
|
||||
if let sessionCredentials = sessionCredentials {
|
||||
var newAccount = false
|
||||
if self.account == nil {
|
||||
self.account = AccountManager.shared.createAccount(type: .newsBlur)
|
||||
@ -106,8 +112,10 @@ class NewsBlurAccountViewController: UITableViewController {
|
||||
|
||||
do {
|
||||
try self.account?.removeCredentials(type: .newsBlurBasic)
|
||||
try self.account?.removeCredentials(type: .newsBlurSessionId)
|
||||
} catch {}
|
||||
try self.account?.storeCredentials(credentials)
|
||||
try self.account?.storeCredentials(basicCredentials)
|
||||
try self.account?.storeCredentials(sessionCredentials)
|
||||
|
||||
if newAccount {
|
||||
self.account?.refreshAll() { result in
|
||||
|
@ -21,7 +21,6 @@ class ReaderAPIAccountViewController: UITableViewController {
|
||||
@IBOutlet weak var showHideButton: UIButton!
|
||||
@IBOutlet weak var actionButton: UIButton!
|
||||
|
||||
|
||||
weak var account: Account?
|
||||
var accountType: AccountType?
|
||||
weak var delegate: AddAccountDismissDelegate?
|
||||
@ -109,22 +108,26 @@ class ReaderAPIAccountViewController: UITableViewController {
|
||||
}
|
||||
|
||||
@IBAction func action(_ sender: Any) {
|
||||
|
||||
validateDataEntry()
|
||||
guard validateDataEntry(), let type = accountType else {
|
||||
return
|
||||
}
|
||||
|
||||
let username = usernameTextField.text!
|
||||
let password = passwordTextField.text!
|
||||
let url = apiURL()!
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: type, username: trimmedUsername) else {
|
||||
showError(NSLocalizedString("There is already an account of that type with that username created.", comment: "Duplicate Error"))
|
||||
return
|
||||
}
|
||||
|
||||
startAnimatingActivityIndicator()
|
||||
disableNavigation()
|
||||
|
||||
// When you fill in the email address via auto-complete it adds extra whitespace
|
||||
let trimmedUsername = username.trimmingCharacters(in: .whitespaces)
|
||||
let credentials = Credentials(type: .readerBasic, username: trimmedUsername, secret: password)
|
||||
guard let type = accountType else {
|
||||
return
|
||||
}
|
||||
Account.validateCredentials(type: type, credentials: credentials, endpoint: url) { result in
|
||||
|
||||
self.stopAnimatingActivityIndicator()
|
||||
@ -201,23 +204,24 @@ class ReaderAPIAccountViewController: UITableViewController {
|
||||
return nil
|
||||
}
|
||||
|
||||
private func validateDataEntry() {
|
||||
private func validateDataEntry() -> Bool {
|
||||
switch accountType {
|
||||
case .freshRSS:
|
||||
if !usernameTextField.hasText || !passwordTextField.hasText || !apiURLTextField.hasText {
|
||||
showError(NSLocalizedString("Username, password, and API URL are required.", comment: "Credentials Error"))
|
||||
return
|
||||
return false
|
||||
}
|
||||
guard let _ = URL(string: apiURLTextField.text!) else {
|
||||
showError(NSLocalizedString("Invalid API URL.", comment: "Invalid API URL"))
|
||||
return
|
||||
return false
|
||||
}
|
||||
default:
|
||||
if !usernameTextField.hasText || !passwordTextField.hasText {
|
||||
showError(NSLocalizedString("Username and password are required.", comment: "Credentials Error"))
|
||||
return
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private func apiURL() -> URL? {
|
||||
|
@ -58,6 +58,25 @@ class AccountInspectorViewController: UITableViewController {
|
||||
addViewController.account = account
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
present(navController, animated: true)
|
||||
case .feedWrangler:
|
||||
let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "FeedWranglerAccountNavigationViewController") as! UINavigationController
|
||||
let addViewController = navController.topViewController as! FeedWranglerAccountViewController
|
||||
addViewController.account = account
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
present(navController, animated: true)
|
||||
case .newsBlur:
|
||||
let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "NewsBlurAccountNavigationViewController") as! UINavigationController
|
||||
let addViewController = navController.topViewController as! NewsBlurAccountViewController
|
||||
addViewController.account = account
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
present(navController, animated: true)
|
||||
case .inoreader, .bazQux, .theOldReader, .freshRSS:
|
||||
let navController = UIStoryboard.account.instantiateViewController(withIdentifier: "ReaderAPIAccountNavigationViewController") as! UINavigationController
|
||||
let addViewController = navController.topViewController as! ReaderAPIAccountViewController
|
||||
addViewController.accountType = account.type
|
||||
addViewController.account = account
|
||||
navController.modalPresentationStyle = .currentContext
|
||||
present(navController, animated: true)
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "204",
|
||||
"green" : "153",
|
||||
"red" : "51"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "194",
|
||||
"green" : "143",
|
||||
"red" : "43"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "178",
|
||||
"green" : "174",
|
||||
"red" : "174"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "102",
|
||||
"green" : "99",
|
||||
"red" : "99"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "206",
|
||||
"green" : "94",
|
||||
"red" : "7"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "192",
|
||||
"green" : "69",
|
||||
"red" : "7"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "76",
|
||||
"green" : "177",
|
||||
"red" : "43"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "56",
|
||||
"green" : "157",
|
||||
"red" : "23"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "188",
|
||||
"green" : "145",
|
||||
"red" : "30"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "178",
|
||||
"green" : "135",
|
||||
"red" : "20"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "189",
|
||||
"green" : "98",
|
||||
"red" : "0"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "169",
|
||||
"green" : "78",
|
||||
"red" : "1"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "199",
|
||||
"green" : "123",
|
||||
"red" : "1"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "179",
|
||||
"green" : "103",
|
||||
"red" : "1"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|