mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-01-24 16:10:41 +01:00
Update LocalAccountDelegate to use new FeedDownloader package. Temporarily comment-out feed refreshing in CloudKitAccountDelegate.
This commit is contained in:
parent
ea3a78b841
commit
33215ba9e3
@ -27,7 +27,8 @@ let package = Package(
|
||||
.package(path: "../LocalAccount"),
|
||||
.package(path: "../FeedFinder"),
|
||||
.package(path: "../Feedly"),
|
||||
.package(path: "../CommonErrors")
|
||||
.package(path: "../CommonErrors"),
|
||||
.package(path: "../FeedDownloader")
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
@ -49,7 +50,8 @@ let package = Package(
|
||||
"LocalAccount",
|
||||
"FeedFinder",
|
||||
"CommonErrors",
|
||||
"Feedly"
|
||||
"Feedly",
|
||||
"FeedDownloader"
|
||||
],
|
||||
swiftSettings: [
|
||||
.enableExperimentalFeature("StrictConcurrency")
|
||||
|
@ -180,6 +180,14 @@ public enum FetchType {
|
||||
return _externalIDToFeedDictionary
|
||||
}
|
||||
|
||||
private var _feedURLToFeedDictionary = [String: Feed]()
|
||||
var feedURLToFeedDictionary: [String: Feed] {
|
||||
if feedDictionariesNeedUpdate {
|
||||
rebuildFeedDictionaries()
|
||||
}
|
||||
return _feedURLToFeedDictionary
|
||||
}
|
||||
|
||||
var flattenedFeedURLs: Set<String> {
|
||||
return Set(flattenedFeeds().map({ $0.url }))
|
||||
}
|
||||
@ -1148,9 +1156,11 @@ private extension Account {
|
||||
func rebuildFeedDictionaries() {
|
||||
var idDictionary = [String: Feed]()
|
||||
var externalIDDictionary = [String: Feed]()
|
||||
|
||||
var urlDictionary = [String: Feed]()
|
||||
|
||||
for feed in flattenedFeeds() {
|
||||
idDictionary[feed.feedID] = feed
|
||||
urlDictionary[feed.url] = feed
|
||||
if let externalID = feed.externalID {
|
||||
externalIDDictionary[externalID] = feed
|
||||
}
|
||||
@ -1158,6 +1168,7 @@ private extension Account {
|
||||
|
||||
_idToFeedDictionary = idDictionary
|
||||
_externalIDToFeedDictionary = externalIDDictionary
|
||||
_feedURLToFeedDictionary = urlDictionary
|
||||
feedDictionariesNeedUpdate = false
|
||||
}
|
||||
|
||||
@ -1319,6 +1330,10 @@ extension Account {
|
||||
public func existingFeed(withExternalID externalID: String) -> Feed? {
|
||||
return externalIDToFeedDictionary[externalID]
|
||||
}
|
||||
|
||||
public func existingFeed(urlString: String) -> Feed? {
|
||||
return feedURLToFeedDictionary[urlString]
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OPMLRepresentable
|
||||
|
@ -48,11 +48,11 @@ enum CloudKitAccountDelegateError: LocalizedError {
|
||||
|
||||
private let mainThreadOperationQueue = MainThreadOperationQueue()
|
||||
|
||||
private lazy var refresher: LocalAccountRefresher = {
|
||||
let refresher = LocalAccountRefresher()
|
||||
refresher.delegate = self
|
||||
return refresher
|
||||
}()
|
||||
// private lazy var refresher: LocalAccountRefresher = {
|
||||
// let refresher = LocalAccountRefresher()
|
||||
// refresher.delegate = self
|
||||
// return refresher
|
||||
// }()
|
||||
|
||||
weak var account: Account?
|
||||
|
||||
@ -459,7 +459,7 @@ enum CloudKitAccountDelegateError: LocalizedError {
|
||||
|
||||
func suspendNetwork() {
|
||||
|
||||
refresher.suspend()
|
||||
// refresher.suspend()
|
||||
}
|
||||
|
||||
func suspendDatabase() {
|
||||
@ -471,7 +471,7 @@ enum CloudKitAccountDelegateError: LocalizedError {
|
||||
|
||||
func resume() {
|
||||
|
||||
refresher.resume()
|
||||
// refresher.resume()
|
||||
|
||||
Task {
|
||||
await database.resume()
|
||||
@ -536,7 +536,7 @@ private extension CloudKitAccountDelegate {
|
||||
|
||||
func combinedRefresh(_ account: Account, _ feeds: Set<Feed>) async throws {
|
||||
|
||||
await refresher.refreshFeeds(feeds)
|
||||
// await refresher.refreshFeeds(feeds)
|
||||
}
|
||||
|
||||
func createRSSFeed(for account: Account, url: URL, editedName: String?, container: Container, validateFeed: Bool) async throws -> Feed {
|
||||
@ -733,22 +733,22 @@ private extension CloudKitAccountDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
|
||||
|
||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: Feed) {
|
||||
|
||||
refreshProgress.completeTask()
|
||||
}
|
||||
|
||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, articleChanges: ArticleChanges, completion: @escaping () -> Void) {
|
||||
|
||||
Task { @MainActor in
|
||||
await storeArticleChanges(new: articleChanges.newArticles,
|
||||
updated: articleChanges.updatedArticles,
|
||||
deleted: articleChanges.deletedArticles)
|
||||
}
|
||||
}
|
||||
}
|
||||
//extension CloudKitAccountDelegate: LocalAccountRefresherDelegate {
|
||||
//
|
||||
// func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: Feed) {
|
||||
//
|
||||
// refreshProgress.completeTask()
|
||||
// }
|
||||
//
|
||||
// func localAccountRefresher(_ refresher: LocalAccountRefresher, articleChanges: ArticleChanges, completion: @escaping () -> Void) {
|
||||
//
|
||||
// Task { @MainActor in
|
||||
// await storeArticleChanges(new: articleChanges.newArticles,
|
||||
// updated: articleChanges.updatedArticles,
|
||||
// deleted: articleChanges.deletedArticles)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
extension CloudKitAccountDelegate: CloudKitFeedInfoDelegate {
|
||||
|
||||
|
@ -18,6 +18,7 @@ import Core
|
||||
import CommonErrors
|
||||
import FeedFinder
|
||||
import LocalAccount
|
||||
import FeedDownloader
|
||||
|
||||
public enum LocalAccountDelegateError: String, Error {
|
||||
case invalidParameter = "An invalid parameter was used."
|
||||
@ -25,16 +26,10 @@ public enum LocalAccountDelegateError: String, Error {
|
||||
|
||||
final class LocalAccountDelegate: AccountDelegate {
|
||||
|
||||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "LocalAccount")
|
||||
private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "LocalAccount")
|
||||
|
||||
weak var account: Account?
|
||||
|
||||
private lazy var refresher: LocalAccountRefresher = {
|
||||
let refresher = LocalAccountRefresher()
|
||||
refresher.delegate = self
|
||||
return refresher
|
||||
}()
|
||||
|
||||
let behaviors: AccountBehaviors = []
|
||||
let isOPMLImportInProgress = false
|
||||
|
||||
@ -42,8 +37,17 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
var credentials: Credentials?
|
||||
var accountMetadata: AccountMetadata?
|
||||
|
||||
let refreshProgress = DownloadProgress(numberOfTasks: 0)
|
||||
|
||||
var refreshProgress: DownloadProgress {
|
||||
feedDownloader.downloadProgress
|
||||
}
|
||||
|
||||
let feedDownloader: FeedDownloader
|
||||
|
||||
init() {
|
||||
self.feedDownloader = FeedDownloader()
|
||||
feedDownloader.delegate = self
|
||||
}
|
||||
|
||||
func receiveRemoteNotification(for account: Account, userInfo: [AnyHashable : Any]) async {
|
||||
}
|
||||
|
||||
@ -54,12 +58,10 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
}
|
||||
|
||||
let feeds = account.flattenedFeeds()
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(feeds.count)
|
||||
let feedURLStrings = feeds.map { $0.url }
|
||||
let feedURLs = Set(feedURLStrings.compactMap { URL(string: $0) })
|
||||
|
||||
await refresher.refreshFeeds(feeds)
|
||||
|
||||
self.refreshProgress.clear()
|
||||
account.metadata.lastArticleFetchEndTime = Date()
|
||||
feedDownloader.downloadFeeds(feedURLs)
|
||||
}
|
||||
|
||||
func syncArticleStatus(for account: Account) async throws {
|
||||
@ -164,7 +166,9 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
// MARK: Suspend and Resume (for iOS)
|
||||
|
||||
func suspendNetwork() {
|
||||
refresher.suspend()
|
||||
Task { @MainActor in
|
||||
await feedDownloader.suspend()
|
||||
}
|
||||
}
|
||||
|
||||
func suspendDatabase() {
|
||||
@ -172,23 +176,10 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
}
|
||||
|
||||
func resume() {
|
||||
refresher.resume()
|
||||
feedDownloader.resume()
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalAccountDelegate: LocalAccountRefresherDelegate {
|
||||
|
||||
|
||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, requestCompletedFor: Feed) {
|
||||
refreshProgress.completeTask()
|
||||
}
|
||||
|
||||
func localAccountRefresher(_ refresher: LocalAccountRefresher, articleChanges: ArticleChanges, completion: @escaping () -> Void) {
|
||||
completion()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension LocalAccountDelegate {
|
||||
|
||||
func createRSSFeed(for account: Account, url: URL, editedName: String?, container: Container) async throws -> Feed {
|
||||
@ -197,9 +188,7 @@ private extension LocalAccountDelegate {
|
||||
// container before the name has been downloaded. This will put it in the sidebar
|
||||
// with an Untitled name if we don't delay it being added to the sidebar.
|
||||
BatchUpdate.shared.start()
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
defer {
|
||||
refreshProgress.completeTask()
|
||||
BatchUpdate.shared.end()
|
||||
}
|
||||
|
||||
@ -227,3 +216,68 @@ private extension LocalAccountDelegate {
|
||||
return feed
|
||||
}
|
||||
}
|
||||
|
||||
extension LocalAccountDelegate: FeedDownloaderDelegate {
|
||||
|
||||
func feedDownloader(_: FeedDownloader, requestCompletedForFeedURL feedURL: URL, response: URLResponse?, data: Data?, error: Error?) {
|
||||
|
||||
guard let account, let feed = account.existingFeed(urlString: feedURL.absoluteString) else {
|
||||
return
|
||||
}
|
||||
|
||||
if let error {
|
||||
logger.debug("Error downloading \(feed.url) - \(error)")
|
||||
return
|
||||
}
|
||||
guard let response, let data, !data.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
parseAndUpdateFeed(feed, response: response, data: data)
|
||||
}
|
||||
|
||||
func feedDownloader(_: FeedDownloader, requestCanceledForFeedURL feedURL: URL, response: URLResponse?, data: Data?, error: Error?, reason: FeedDownloader.CancellationReason) {
|
||||
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
func feedDownloaderSessionDidComplete(_: FeedDownloader) {
|
||||
|
||||
account?.metadata.lastArticleFetchEndTime = Date()
|
||||
}
|
||||
|
||||
func feedDownloader(_: FeedDownloader, conditionalGetInfoFor feedURL: URL) -> HTTPConditionalGetInfo? {
|
||||
|
||||
guard let feed = account?.existingFeed(urlString: feedURL.absoluteString) else {
|
||||
return nil
|
||||
}
|
||||
return feed.conditionalGetInfo
|
||||
}
|
||||
}
|
||||
|
||||
private extension LocalAccountDelegate {
|
||||
|
||||
func parseAndUpdateFeed(_ feed: Feed, response: URLResponse, data: Data) {
|
||||
|
||||
Task { @MainActor in
|
||||
|
||||
let dataHash = data.md5String
|
||||
if dataHash == feed.contentHash {
|
||||
return
|
||||
}
|
||||
|
||||
let parserData = ParserData(url: feed.url, data: data)
|
||||
|
||||
guard let parsedFeed = try? await FeedParser.parse(parserData) else {
|
||||
return
|
||||
}
|
||||
|
||||
try await self.account?.update(feed: feed, with: parsedFeed)
|
||||
|
||||
if let httpResponse = response as? HTTPURLResponse {
|
||||
feed.conditionalGetInfo = HTTPConditionalGetInfo(urlResponse: httpResponse)
|
||||
}
|
||||
feed.contentHash = dataHash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user