Merge branch 'ios-candidate' of https://github.com/Ranchero-Software/NetNewsWire into ios-candidate
This commit is contained in:
commit
b7368c2c90
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
import Foundation
|
||||
|
||||
struct FeedlyCategory: Decodable {
|
||||
var label: String
|
||||
var id: String
|
||||
let label: String
|
||||
let id: String
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 article’s 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 author’s 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 feed’s 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 feed’s 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]?
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import Articles
|
|||
import RSParser
|
||||
|
||||
struct FeedlyEntryParser {
|
||||
var entry: FeedlyEntry
|
||||
let entry: FeedlyEntry
|
||||
|
||||
var id: String {
|
||||
return entry.id
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
import Foundation
|
||||
|
||||
struct FeedlyTag: Decodable {
|
||||
var id: String
|
||||
var label: String?
|
||||
let id: String
|
||||
let label: String?
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue