Merge branch 'ios-candidate' of https://github.com/Ranchero-Software/NetNewsWire into ios-candidate

This commit is contained in:
Maurice Parker 2020-01-17 10:54:21 -07:00
commit b7368c2c90
17 changed files with 76 additions and 111 deletions

View File

@ -45,7 +45,7 @@ class FeedlySyncStreamContentsOperationTests: XCTestCase {
XCTAssertNil(serviceUnreadOnly)
}
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, newerThan: newerThan, log: support.log)
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
syncStreamContents.completionBlock = { _ in
@ -79,7 +79,7 @@ class FeedlySyncStreamContentsOperationTests: XCTestCase {
XCTAssertNil(serviceUnreadOnly)
}
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, newerThan: newerThan, log: support.log)
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
syncStreamContents.completionBlock = { _ in
@ -120,7 +120,7 @@ class FeedlySyncStreamContentsOperationTests: XCTestCase {
getStreamPageExpectation.fulfill()
}
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, newerThan: newerThan, log: support.log)
let syncStreamContents = FeedlySyncStreamContentsOperation(account: account, resource: resource, service: service, isPagingEnabled: true, newerThan: newerThan, log: support.log)
let completionExpectation = expectation(description: "Did Finish")
syncStreamContents.completionBlock = { _ in

View File

@ -10,7 +10,6 @@ import Foundation
struct FeedlyFeedContainerValidator {
var container: Container
var userId: String?
func getValidContainer() throws -> (Folder, String) {
guard let folder = container as? Folder else {
@ -21,16 +20,6 @@ struct FeedlyFeedContainerValidator {
throw FeedlyAccountDelegateError.addFeedInvalidFolder(folder)
}
guard let userId = userId else {
throw FeedlyAccountDelegateError.notLoggedIn
}
let uncategorized = FeedlyCategoryResourceId.Global.uncategorized(for: userId)
guard collectionId != uncategorized.id else {
throw FeedlyAccountDelegateError.addFeedInvalidFolder(folder)
}
return (folder, collectionId)
}
}

View File

@ -9,6 +9,6 @@
import Foundation
struct FeedlyCategory: Decodable {
var label: String
var id: String
let label: String
let id: String
}

View File

@ -9,7 +9,7 @@
import Foundation
struct FeedlyCollection: Codable {
var feeds: [FeedlyFeed]
var label: String
var id: String
let feeds: [FeedlyFeed]
let label: String
let id: String
}

View File

@ -8,84 +8,58 @@
import Foundation
enum Direction: String, Codable {
case leftToRight = "ltr"
case rightToLeft = "rtl"
}
struct FeedlyEntry: Decodable {
/// the unique, immutable ID for this particular article.
var id: String
let id: String
/// the articles title. This string does not contain any HTML markup.
var title: String?
let title: String?
struct Content: Codable {
var content: String?
var direction: Direction?
struct Content: Decodable {
enum Direction: String, Decodable {
case leftToRight = "ltr"
case rightToLeft = "rtl"
}
let content: String?
let direction: Direction?
}
/// This object typically has two values: content for the content itself, and direction (ltr for left-to-right, rtl for right-to-left). The content itself contains sanitized HTML markup.
var content: Content?
let content: Content?
/// content object the article summary. See the content object above.
var summary: Content?
let summary: Content?
/// the authors name
var author: String?
let author: String?
/// the immutable timestamp, in ms, when this article was processed by the feedly Cloud servers.
var crawled: Date
let crawled: Date
/// the timestamp, in ms, when this article was re-processed and updated by the feedly Cloud servers.
var recrawled: Date?
let recrawled: Date?
/// the timestamp, in ms, when this article was published, as reported by the RSS feed (often inaccurate).
// var published: Date
/// the timestamp, in ms, when this article was updated, as reported by the RSS feed
// var updated: Date?
/// the feed from which this article was crawled. If present, streamId will contain the feed id, title will contain the feed title, and htmlUrl will contain the feeds website.
var origin: FeedlyOrigin?
let origin: FeedlyOrigin?
/// Used to help find the URL to visit an article on a web site.
/// See https://groups.google.com/forum/#!searchin/feedly-cloud/feed$20url%7Csort:date/feedly-cloud/Rx3dVd4aTFQ/Hf1ZfLJoCQAJ
var canonical: [FeedlyLink]?
let canonical: [FeedlyLink]?
/// a list of alternate links for this article. Each link object contains a media type and a URL. Typically, a single object is present, with a link to the original web page.
var alternate: [FeedlyLink]?
//
// // var origin:
// // Optional origin object the feed from which this article was crawled. If present, streamId will contain the feed id, title will contain the feed title, and htmlUrl will contain the feeds website.
// var keywords: [String]?
//
// /// an image URL for this entry. If present, url will contain the image URL, width and height its dimension, and contentType its MIME type.
// var visual: Image?
//
let alternate: [FeedlyLink]?
/// Was this entry read by the user? If an Authorization header is not provided, this will always return false. If an Authorization header is provided, it will reflect if the user has read this entry or not.
var unread: Bool
//
/// a list of tag objects (id and label) that the user added to this entry. This value is only returned if an Authorization header is provided, and at least one tag has been added. If the entry has been explicitly marked as read (not the feed itself), the global.read tag will be present.
var tags: [FeedlyTag]?
//
/// a list of category objects (id and label) that the user associated with the feed of this entry. This value is only returned if an Authorization header is provided.
var categories: [FeedlyCategory]?
//
// /// an indicator of how popular this entry is. The higher the number, the more readers have read, saved or shared this particular entry.
// var engagement: Int?
//
// /// Timestamp for tagged articles, contains the timestamp when the article was tagged by the user. This will only be returned when the entry is returned through the streams API.
// var actionTimestamp: Date?
//
/// A list of media links (videos, images, sound etc) provided by the feed. Some entries do not have a summary or content, only a collection of media links.
var enclosure: [FeedlyLink]?
//
// /// The article fingerprint. This value might change if the article is updated.
// var fingerprint: String
// originId
// string the unique id of this post in the RSS feed (not necessarily a URL!)
// sid
// Optional string an internal search id.
let unread: Bool
/// a list of tag objects (id and label) that the user added to this entry. This value is only returned if an Authorization header is provided, and at least one tag has been added. If the entry has been explicitly marked as read (not the feed itself), the global.read tag will be present.
let tags: [FeedlyTag]?
/// a list of category objects (id and label) that the user associated with the feed of this entry. This value is only returned if an Authorization header is provided.
let categories: [FeedlyCategory]?
/// A list of media links (videos, images, sound etc) provided by the feed. Some entries do not have a summary or content, only a collection of media links.
let enclosure: [FeedlyLink]?
}

View File

@ -11,7 +11,7 @@ import Articles
import RSParser
struct FeedlyEntryParser {
var entry: FeedlyEntry
let entry: FeedlyEntry
var id: String {
return entry.id

View File

@ -9,8 +9,8 @@
import Foundation
struct FeedlyFeed: Codable {
var id: String
var title: String?
var updated: Date?
var website: String?
let id: String
let title: String?
let updated: Date?
let website: String?
}

View File

@ -11,9 +11,9 @@ import Foundation
struct FeedlyFeedsSearchResponse: Decodable {
struct Feed: Decodable {
var title: String
var feedId: String
let title: String
let feedId: String
}
var results: [Feed]
let results: [Feed]
}

View File

@ -9,10 +9,10 @@
import Foundation
struct FeedlyLink: Decodable {
var href: String
let href: String
/// The mime type of the resource located by `href`.
/// When `nil`, it's probably a web page?
/// https://groups.google.com/forum/#!searchin/feedly-cloud/feed$20url%7Csort:date/feedly-cloud/Rx3dVd4aTFQ/Hf1ZfLJoCQAJ
var type: String?
let type: String?
}

View File

@ -9,7 +9,7 @@
import Foundation
struct FeedlyOrigin: Decodable {
var title: String?
var streamId: String?
var htmlUrl: String?
let title: String?
let streamId: String?
let htmlUrl: String?
}

View File

@ -17,7 +17,7 @@ protocol FeedlyResourceId {
/// The Feed Resource is documented here: https://developer.feedly.com/cloud/
struct FeedlyFeedResourceId: FeedlyResourceId {
var id: String
let id: String
/// The location of the kind of resource a concrete type represents.
/// If the conrete type cannot strip the resource type from the Id, it should just return the Id
@ -45,7 +45,7 @@ extension FeedlyFeedResourceId {
}
struct FeedlyCategoryResourceId: FeedlyResourceId {
var id: String
let id: String
enum Global {
@ -72,7 +72,7 @@ struct FeedlyCategoryResourceId: FeedlyResourceId {
}
struct FeedlyTagResourceId: FeedlyResourceId {
var id: String
let id: String
enum Global {

View File

@ -9,16 +9,16 @@
import Foundation
struct FeedlyStream: Decodable {
var id: String
let id: String
/// Of the most recent entry for this stream (regardless of continuation, newerThan, etc).
var updated: Date?
let updated: Date?
/// Optional string the continuation id to pass to the next stream call, for pagination.
/// the continuation id to pass to the next stream call, for pagination.
/// This id guarantees that no entry will be duplicated in a stream (meaning, there is no need to de-duplicate entries returned by this call).
/// If this value is not returned, it means the end of the stream has been reached.
var continuation: String?
var items: [FeedlyEntry]
let continuation: String?
let items: [FeedlyEntry]
var isStreamEnd: Bool {
return continuation == nil

View File

@ -9,8 +9,8 @@
import Foundation
struct FeedlyStreamIds: Decodable {
var continuation: String?
var ids: [String]
let continuation: String?
let ids: [String]
var isStreamEnd: Bool {
return continuation == nil

View File

@ -9,6 +9,6 @@
import Foundation
struct FeedlyTag: Decodable {
var id: String
var label: String?
let id: String
let label: String?
}

View File

@ -18,7 +18,7 @@ class FeedlyAddExistingFeedOperation: FeedlyOperation, FeedlyOperationDelegate,
init(account: Account, credentials: Credentials, resource: FeedlyFeedResourceId, service: FeedlyAddFeedToCollectionService, container: Container, progress: DownloadProgress, log: OSLog) throws {
let validator = FeedlyFeedContainerValidator(container: container, userId: credentials.username)
let validator = FeedlyFeedContainerValidator(container: container)
let (folder, collectionId) = try validator.getValidContainer()
self.operationQueue = MainThreadOperationQueue()

View File

@ -30,7 +30,7 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
init(account: Account, credentials: Credentials, url: String, feedName: String?, searchService: FeedlySearchService, addToCollectionService: FeedlyAddFeedToCollectionService, syncUnreadIdsService: FeedlyGetStreamIdsService, getStreamContentsService: FeedlyGetStreamContentsService, database: SyncDatabase, container: Container, progress: DownloadProgress, log: OSLog) throws {
let validator = FeedlyFeedContainerValidator(container: container, userId: credentials.username)
let validator = FeedlyFeedContainerValidator(container: container)
(self.folder, self.collectionId) = try validator.getValidContainer()
self.url = url
@ -83,28 +83,28 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
let addRequest = FeedlyAddFeedToCollectionOperation(account: account, folder: folder, feedResource: feedResourceId, feedName: feedName, collectionId: collectionId, service: addToCollectionService)
addRequest.delegate = self
addRequest.downloadProgress = downloadProgress
self.operationQueue.addOperation(addRequest)
operationQueue.addOperation(addRequest)
let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: addRequest, log: log)
operationQueue.make(createFeeds, dependOn: addRequest)
createFeeds.downloadProgress = downloadProgress
self.operationQueue.addOperation(createFeeds)
operationQueue.addOperation(createFeeds)
let syncUnread = FeedlyIngestUnreadArticleIdsOperation(account: account, credentials: credentials, service: syncUnreadIdsService, database: database, newerThan: nil, log: log)
operationQueue.make(syncUnread, dependOn: createFeeds)
syncUnread.downloadProgress = downloadProgress
self.operationQueue.addOperation(syncUnread)
operationQueue.addOperation(syncUnread)
let syncFeed = FeedlySyncStreamContentsOperation(account: account, resource: feedResourceId, service: getStreamContentsService, newerThan: nil, log: log)
let syncFeed = FeedlySyncStreamContentsOperation(account: account, resource: feedResourceId, service: getStreamContentsService, isPagingEnabled: false, newerThan: nil, log: log)
operationQueue.make(syncFeed, dependOn: syncUnread)
syncFeed.downloadProgress = downloadProgress
self.operationQueue.addOperation(syncFeed)
operationQueue.addOperation(syncFeed)
let finishOperation = FeedlyCheckpointOperation()
finishOperation.checkpointDelegate = self
finishOperation.downloadProgress = downloadProgress
operationQueue.make(finishOperation, dependOn: syncFeed)
self.operationQueue.addOperation(finishOperation)
operationQueue.addOperation(finishOperation)
}
func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) {

View File

@ -17,13 +17,15 @@ final class FeedlySyncStreamContentsOperation: FeedlyOperation, FeedlyOperationD
private let operationQueue = MainThreadOperationQueue()
private let service: FeedlyGetStreamContentsService
private let newerThan: Date?
private let isPagingEnabled: Bool
private let log: OSLog
private let finishOperation: FeedlyCheckpointOperation
init(account: Account, resource: FeedlyResourceId, service: FeedlyGetStreamContentsService, newerThan: Date?, log: OSLog) {
init(account: Account, resource: FeedlyResourceId, service: FeedlyGetStreamContentsService, isPagingEnabled: Bool, newerThan: Date?, log: OSLog) {
self.account = account
self.resource = resource
self.service = service
self.isPagingEnabled = isPagingEnabled
self.operationQueue.suspend()
self.newerThan = newerThan
self.log = log
@ -38,7 +40,7 @@ final class FeedlySyncStreamContentsOperation: FeedlyOperation, FeedlyOperationD
convenience init(account: Account, credentials: Credentials, service: FeedlyGetStreamContentsService, newerThan: Date?, log: OSLog) {
let all = FeedlyCategoryResourceId.Global.all(for: credentials.username)
self.init(account: account, resource: all, service: service, newerThan: newerThan, log: log)
self.init(account: account, resource: all, service: service, isPagingEnabled: true, newerThan: newerThan, log: log)
}
override func cancel() {
@ -98,7 +100,7 @@ final class FeedlySyncStreamContentsOperation: FeedlyOperation, FeedlyOperationD
os_log(.debug, log: log, "Ingesting %i items from %@", stream.items.count, stream.id)
guard let continuation = stream.continuation else {
guard isPagingEnabled, let continuation = stream.continuation else {
os_log(.debug, log: log, "Reached end of stream for %@", stream.id)
return
}