From 1e578828e9bb812eef73981fe12be5bea8aae01c Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sat, 6 Feb 2021 07:06:45 +0800 Subject: [PATCH 01/17] : class chnaged to : AnyObject for protocols --- Account/Sources/Account/AccountMetadata.swift | 2 +- Account/Sources/Account/Container.swift | 2 +- .../Sources/Account/FeedProvider/FeedProviderManager.swift | 2 +- .../Reddit/RedditFeedProviderTokenRefreshOperation.swift | 2 +- Account/Sources/Account/Feedly/FeedlyAPICaller.swift | 2 +- .../Feedly/Models/FeedlyEntryIdentifierProviding.swift | 2 +- .../Account/Feedly/OAuthAccountAuthorizationOperation.swift | 2 +- .../Sources/Account/Feedly/OAuthAcessTokenRefreshing.swift | 2 +- .../Account/Feedly/Operations/FeedlyCheckpointOperation.swift | 2 +- .../Feedly/Operations/FeedlyGetCollectionsOperation.swift | 2 +- .../Feedly/Operations/FeedlyGetStreamContentsOperation.swift | 2 +- .../Feedly/Operations/FeedlyGetStreamIdsOperation.swift | 2 +- .../Sources/Account/Feedly/Operations/FeedlyOperation.swift | 2 +- .../Feedly/Operations/FeedlyRequestStreamsOperation.swift | 2 +- .../Account/Feedly/Operations/FeedlySearchOperation.swift | 4 ++-- .../Account/Feedly/Services/FeedlyGetCollectionsService.swift | 2 +- .../Account/Feedly/Services/FeedlyGetEntriesService.swift | 2 +- .../Feedly/Services/FeedlyGetStreamContentsService.swift | 2 +- .../Account/Feedly/Services/FeedlyGetStreamIdsService.swift | 2 +- .../Account/Feedly/Services/FeedlyMarkArticlesService.swift | 2 +- Account/Sources/Account/WebFeedMetadata.swift | 2 +- Account/Tests/AccountTests/TestTransport.swift | 2 +- Mac/Inspector/InspectorWindowController.swift | 2 +- Mac/MainWindow/AddFeed/AddFeedWIndowController.swift | 2 +- Mac/MainWindow/Detail/DetailWebViewController.swift | 2 +- Mac/MainWindow/Sidebar/SidebarViewController.swift | 2 +- Mac/MainWindow/Timeline/TimelineContainerViewController.swift | 2 +- Mac/MainWindow/Timeline/TimelineViewController.swift | 2 +- .../ExtensionPointPreferencesViewController.swift | 2 +- Multiplatform/Shared/Sidebar/SidebarModel.swift | 2 +- Multiplatform/Shared/Timeline/TimelineModel.swift | 2 +- Multiplatform/iOS/Article/WebViewController.swift | 2 +- Multiplatform/macOS/Article/WebViewController.swift | 2 +- Shared/SmartFeeds/PseudoFeed.swift | 4 ++-- iOS/Article/WebViewController.swift | 2 +- iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift | 2 +- iOS/ShareExtension/ShareFolderPickerController.swift | 2 +- 37 files changed, 39 insertions(+), 39 deletions(-) diff --git a/Account/Sources/Account/AccountMetadata.swift b/Account/Sources/Account/AccountMetadata.swift index f4c1a2cdb..4fa08063a 100644 --- a/Account/Sources/Account/AccountMetadata.swift +++ b/Account/Sources/Account/AccountMetadata.swift @@ -9,7 +9,7 @@ import Foundation import RSWeb -protocol AccountMetadataDelegate: class { +protocol AccountMetadataDelegate: AnyObject { func valueDidChange(_ accountMetadata: AccountMetadata, key: AccountMetadata.CodingKeys) } diff --git a/Account/Sources/Account/Container.swift b/Account/Sources/Account/Container.swift index 7c4eda9c8..1a17158ee 100644 --- a/Account/Sources/Account/Container.swift +++ b/Account/Sources/Account/Container.swift @@ -16,7 +16,7 @@ extension Notification.Name { public static let ChildrenDidChange = Notification.Name("ChildrenDidChange") } -public protocol Container: class, ContainerIdentifiable { +public protocol Container: AnyObject, ContainerIdentifiable { var account: Account? { get } var topLevelWebFeeds: Set { get set } diff --git a/Account/Sources/Account/FeedProvider/FeedProviderManager.swift b/Account/Sources/Account/FeedProvider/FeedProviderManager.swift index 4177f550c..f4b6e1cf4 100644 --- a/Account/Sources/Account/FeedProvider/FeedProviderManager.swift +++ b/Account/Sources/Account/FeedProvider/FeedProviderManager.swift @@ -8,7 +8,7 @@ import Foundation -public protocol FeedProviderManagerDelegate: class { +public protocol FeedProviderManagerDelegate: AnyObject { var activeFeedProviders: [FeedProvider] { get } } diff --git a/Account/Sources/Account/FeedProvider/Reddit/RedditFeedProviderTokenRefreshOperation.swift b/Account/Sources/Account/FeedProvider/Reddit/RedditFeedProviderTokenRefreshOperation.swift index 1b2b6bc73..6a90e9984 100644 --- a/Account/Sources/Account/FeedProvider/Reddit/RedditFeedProviderTokenRefreshOperation.swift +++ b/Account/Sources/Account/FeedProvider/Reddit/RedditFeedProviderTokenRefreshOperation.swift @@ -11,7 +11,7 @@ import RSCore import OAuthSwift import Secrets -protocol RedditFeedProviderTokenRefreshOperationDelegate: class { +protocol RedditFeedProviderTokenRefreshOperationDelegate: AnyObject { var username: String? { get } var oauthTokenLastRefresh: Date? { get set } var oauthToken: String { get set } diff --git a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift index 36f974835..635bbe0bc 100644 --- a/Account/Sources/Account/Feedly/FeedlyAPICaller.swift +++ b/Account/Sources/Account/Feedly/FeedlyAPICaller.swift @@ -10,7 +10,7 @@ import Foundation import RSWeb import Secrets -protocol FeedlyAPICallerDelegate: class { +protocol FeedlyAPICallerDelegate: AnyObject { /// Implemented by the `FeedlyAccountDelegate` reauthorize the client with a fresh OAuth token so the client can retry the unauthorized request. /// Pass `true` to the completion handler if the failing request should be retried with a fresh token or `false` if the unauthorized request should complete with the original failure error. func reauthorizeFeedlyAPICaller(_ caller: FeedlyAPICaller, completionHandler: @escaping (Bool) -> ()) diff --git a/Account/Sources/Account/Feedly/Models/FeedlyEntryIdentifierProviding.swift b/Account/Sources/Account/Feedly/Models/FeedlyEntryIdentifierProviding.swift index 4ad0054ec..a87fbc4ea 100644 --- a/Account/Sources/Account/Feedly/Models/FeedlyEntryIdentifierProviding.swift +++ b/Account/Sources/Account/Feedly/Models/FeedlyEntryIdentifierProviding.swift @@ -8,7 +8,7 @@ import Foundation -protocol FeedlyEntryIdentifierProviding: class { +protocol FeedlyEntryIdentifierProviding: AnyObject { var entryIds: Set { get } } diff --git a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift index 4378bedc5..f16029ce8 100644 --- a/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift +++ b/Account/Sources/Account/Feedly/OAuthAccountAuthorizationOperation.swift @@ -10,7 +10,7 @@ import Foundation import AuthenticationServices import RSCore -public protocol OAuthAccountAuthorizationOperationDelegate: class { +public protocol OAuthAccountAuthorizationOperationDelegate: AnyObject { func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didCreate account: Account) func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) } diff --git a/Account/Sources/Account/Feedly/OAuthAcessTokenRefreshing.swift b/Account/Sources/Account/Feedly/OAuthAcessTokenRefreshing.swift index f7b862375..d464ee405 100644 --- a/Account/Sources/Account/Feedly/OAuthAcessTokenRefreshing.swift +++ b/Account/Sources/Account/Feedly/OAuthAcessTokenRefreshing.swift @@ -40,7 +40,7 @@ public protocol OAuthAcessTokenRefreshRequesting { } /// Implemented by concrete types to perform the actual request. -protocol OAuthAccessTokenRefreshing: class { +protocol OAuthAccessTokenRefreshing: AnyObject { func refreshAccessToken(with refreshToken: String, client: OAuthAuthorizationClient, completion: @escaping (Result) -> ()) } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlyCheckpointOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlyCheckpointOperation.swift index d577abeae..5d32d76ec 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlyCheckpointOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlyCheckpointOperation.swift @@ -8,7 +8,7 @@ import Foundation -protocol FeedlyCheckpointOperationDelegate: class { +protocol FeedlyCheckpointOperationDelegate: AnyObject { func feedlyCheckpointOperationDidReachCheckpoint(_ operation: FeedlyCheckpointOperation) } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlyGetCollectionsOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlyGetCollectionsOperation.swift index 8477bb910..151fc2e28 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlyGetCollectionsOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlyGetCollectionsOperation.swift @@ -9,7 +9,7 @@ import Foundation import os.log -protocol FeedlyCollectionProviding: class { +protocol FeedlyCollectionProviding: AnyObject { var collections: [FeedlyCollection] { get } } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamContentsOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamContentsOperation.swift index 4c67bcfd4..7cdde6576 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamContentsOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamContentsOperation.swift @@ -19,7 +19,7 @@ protocol FeedlyParsedItemProviding { var parsedEntries: Set { get } } -protocol FeedlyGetStreamContentsOperationDelegate: class { +protocol FeedlyGetStreamContentsOperationDelegate: AnyObject { func feedlyGetStreamContentsOperation(_ operation: FeedlyGetStreamContentsOperation, didGetContentsOf stream: FeedlyStream) } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamIdsOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamIdsOperation.swift index 4f6fea202..602520720 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamIdsOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlyGetStreamIdsOperation.swift @@ -9,7 +9,7 @@ import Foundation import os.log -protocol FeedlyGetStreamIdsOperationDelegate: class { +protocol FeedlyGetStreamIdsOperationDelegate: AnyObject { func feedlyGetStreamIdsOperation(_ operation: FeedlyGetStreamIdsOperation, didGet streamIds: FeedlyStreamIds) } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlyOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlyOperation.swift index e25ae63b0..a4d1c0ff1 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlyOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlyOperation.swift @@ -10,7 +10,7 @@ import Foundation import RSWeb import RSCore -protocol FeedlyOperationDelegate: class { +protocol FeedlyOperationDelegate: AnyObject { func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlyRequestStreamsOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlyRequestStreamsOperation.swift index 0cb7731ef..688546abb 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlyRequestStreamsOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlyRequestStreamsOperation.swift @@ -9,7 +9,7 @@ import Foundation import os.log -protocol FeedlyRequestStreamsOperationDelegate: class { +protocol FeedlyRequestStreamsOperationDelegate: AnyObject { func feedlyRequestStreamsOperation(_ operation: FeedlyRequestStreamsOperation, enqueue collectionStreamOperation: FeedlyGetStreamContentsOperation) } diff --git a/Account/Sources/Account/Feedly/Operations/FeedlySearchOperation.swift b/Account/Sources/Account/Feedly/Operations/FeedlySearchOperation.swift index 5e9bfde85..40d6c76ec 100644 --- a/Account/Sources/Account/Feedly/Operations/FeedlySearchOperation.swift +++ b/Account/Sources/Account/Feedly/Operations/FeedlySearchOperation.swift @@ -8,11 +8,11 @@ import Foundation -protocol FeedlySearchService: class { +protocol FeedlySearchService: AnyObject { func getFeeds(for query: String, count: Int, locale: String, completion: @escaping (Result) -> ()) } -protocol FeedlySearchOperationDelegate: class { +protocol FeedlySearchOperationDelegate: AnyObject { func feedlySearchOperation(_ operation: FeedlySearchOperation, didGet response: FeedlyFeedsSearchResponse) } diff --git a/Account/Sources/Account/Feedly/Services/FeedlyGetCollectionsService.swift b/Account/Sources/Account/Feedly/Services/FeedlyGetCollectionsService.swift index 5714e63c5..2dbf6861b 100644 --- a/Account/Sources/Account/Feedly/Services/FeedlyGetCollectionsService.swift +++ b/Account/Sources/Account/Feedly/Services/FeedlyGetCollectionsService.swift @@ -8,6 +8,6 @@ import Foundation -protocol FeedlyGetCollectionsService: class { +protocol FeedlyGetCollectionsService: AnyObject { func getCollections(completion: @escaping (Result<[FeedlyCollection], Error>) -> ()) } diff --git a/Account/Sources/Account/Feedly/Services/FeedlyGetEntriesService.swift b/Account/Sources/Account/Feedly/Services/FeedlyGetEntriesService.swift index 5acc75663..7bc00b607 100644 --- a/Account/Sources/Account/Feedly/Services/FeedlyGetEntriesService.swift +++ b/Account/Sources/Account/Feedly/Services/FeedlyGetEntriesService.swift @@ -8,6 +8,6 @@ import Foundation -protocol FeedlyGetEntriesService: class { +protocol FeedlyGetEntriesService: AnyObject { func getEntries(for ids: Set, completion: @escaping (Result<[FeedlyEntry], Error>) -> ()) } diff --git a/Account/Sources/Account/Feedly/Services/FeedlyGetStreamContentsService.swift b/Account/Sources/Account/Feedly/Services/FeedlyGetStreamContentsService.swift index ce80d59dc..6770a2593 100644 --- a/Account/Sources/Account/Feedly/Services/FeedlyGetStreamContentsService.swift +++ b/Account/Sources/Account/Feedly/Services/FeedlyGetStreamContentsService.swift @@ -8,6 +8,6 @@ import Foundation -protocol FeedlyGetStreamContentsService: class { +protocol FeedlyGetStreamContentsService: AnyObject { func getStreamContents(for resource: FeedlyResourceId, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result) -> ()) } diff --git a/Account/Sources/Account/Feedly/Services/FeedlyGetStreamIdsService.swift b/Account/Sources/Account/Feedly/Services/FeedlyGetStreamIdsService.swift index c5889cbe1..3d5863e0d 100644 --- a/Account/Sources/Account/Feedly/Services/FeedlyGetStreamIdsService.swift +++ b/Account/Sources/Account/Feedly/Services/FeedlyGetStreamIdsService.swift @@ -8,6 +8,6 @@ import Foundation -protocol FeedlyGetStreamIdsService: class { +protocol FeedlyGetStreamIdsService: AnyObject { func getStreamIds(for resource: FeedlyResourceId, continuation: String?, newerThan: Date?, unreadOnly: Bool?, completion: @escaping (Result) -> ()) } diff --git a/Account/Sources/Account/Feedly/Services/FeedlyMarkArticlesService.swift b/Account/Sources/Account/Feedly/Services/FeedlyMarkArticlesService.swift index 6220d147c..60a9263c2 100644 --- a/Account/Sources/Account/Feedly/Services/FeedlyMarkArticlesService.swift +++ b/Account/Sources/Account/Feedly/Services/FeedlyMarkArticlesService.swift @@ -30,6 +30,6 @@ enum FeedlyMarkAction: String { } } -protocol FeedlyMarkArticlesService: class { +protocol FeedlyMarkArticlesService: AnyObject { func mark(_ articleIds: Set, as action: FeedlyMarkAction, completion: @escaping (Result) -> ()) } diff --git a/Account/Sources/Account/WebFeedMetadata.swift b/Account/Sources/Account/WebFeedMetadata.swift index 3c2b2ed46..aa381709f 100644 --- a/Account/Sources/Account/WebFeedMetadata.swift +++ b/Account/Sources/Account/WebFeedMetadata.swift @@ -10,7 +10,7 @@ import Foundation import RSWeb import Articles -protocol WebFeedMetadataDelegate: class { +protocol WebFeedMetadataDelegate: AnyObject { func valueDidChange(_ feedMetadata: WebFeedMetadata, key: WebFeedMetadata.CodingKeys) } diff --git a/Account/Tests/AccountTests/TestTransport.swift b/Account/Tests/AccountTests/TestTransport.swift index 330d0ea37..d6070ef35 100644 --- a/Account/Tests/AccountTests/TestTransport.swift +++ b/Account/Tests/AccountTests/TestTransport.swift @@ -10,7 +10,7 @@ import Foundation import RSWeb import XCTest -protocol TestTransportMockResponseProviding: class { +protocol TestTransportMockResponseProviding: AnyObject { func mockResponseFileUrl(for components: URLComponents) -> URL? } diff --git a/Mac/Inspector/InspectorWindowController.swift b/Mac/Inspector/InspectorWindowController.swift index 8f4f82e8f..2686f86c4 100644 --- a/Mac/Inspector/InspectorWindowController.swift +++ b/Mac/Inspector/InspectorWindowController.swift @@ -8,7 +8,7 @@ import AppKit -protocol Inspector: class { +protocol Inspector: AnyObject { var objects: [Any]? { get set } var isFallbackInspector: Bool { get } // Can handle nothing-to-inspect or unexpected type of objects. diff --git a/Mac/MainWindow/AddFeed/AddFeedWIndowController.swift b/Mac/MainWindow/AddFeed/AddFeedWIndowController.swift index cbeddaa55..1651ec79f 100644 --- a/Mac/MainWindow/AddFeed/AddFeedWIndowController.swift +++ b/Mac/MainWindow/AddFeed/AddFeedWIndowController.swift @@ -15,7 +15,7 @@ enum AddFeedWindowControllerType { case twitterFeed } -protocol AddFeedWindowControllerDelegate: class { +protocol AddFeedWindowControllerDelegate: AnyObject { // userEnteredURL will have already been validated and normalized. func addFeedWindowController(_: AddFeedWindowController, userEnteredURL: URL, userEnteredTitle: String?, container: Container) diff --git a/Mac/MainWindow/Detail/DetailWebViewController.swift b/Mac/MainWindow/Detail/DetailWebViewController.swift index b1f8de853..7e71cfff1 100644 --- a/Mac/MainWindow/Detail/DetailWebViewController.swift +++ b/Mac/MainWindow/Detail/DetailWebViewController.swift @@ -12,7 +12,7 @@ import RSCore import RSWeb import Articles -protocol DetailWebViewControllerDelegate: class { +protocol DetailWebViewControllerDelegate: AnyObject { func mouseDidEnter(_: DetailWebViewController, link: String) func mouseDidExit(_: DetailWebViewController, link: String) } diff --git a/Mac/MainWindow/Sidebar/SidebarViewController.swift b/Mac/MainWindow/Sidebar/SidebarViewController.swift index 277be185e..ab9b4ac0c 100644 --- a/Mac/MainWindow/Sidebar/SidebarViewController.swift +++ b/Mac/MainWindow/Sidebar/SidebarViewController.swift @@ -16,7 +16,7 @@ extension Notification.Name { static let appleSideBarDefaultIconSizeChanged = Notification.Name("AppleSideBarDefaultIconSizeChanged") } -protocol SidebarDelegate: class { +protocol SidebarDelegate: AnyObject { func sidebarSelectionDidChange(_: SidebarViewController, selectedObjects: [AnyObject]?) func unreadCount(for: AnyObject) -> Int func sidebarInvalidatedRestorationState(_: SidebarViewController) diff --git a/Mac/MainWindow/Timeline/TimelineContainerViewController.swift b/Mac/MainWindow/Timeline/TimelineContainerViewController.swift index b43b210fd..c697a1a0c 100644 --- a/Mac/MainWindow/Timeline/TimelineContainerViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineContainerViewController.swift @@ -10,7 +10,7 @@ import AppKit import Account import Articles -protocol TimelineContainerViewControllerDelegate: class { +protocol TimelineContainerViewControllerDelegate: AnyObject { func timelineSelectionDidChange(_: TimelineContainerViewController, articles: [Article]?, mode: TimelineSourceMode) func timelineRequestedWebFeedSelection(_: TimelineContainerViewController, webFeed: WebFeed) func timelineInvalidatedRestorationState(_: TimelineContainerViewController) diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 115a921f9..a664ffc7a 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -12,7 +12,7 @@ import Articles import Account import os.log -protocol TimelineDelegate: class { +protocol TimelineDelegate: AnyObject { func timelineSelectionDidChange(_: TimelineViewController, selectedArticles: [Article]?) func timelineRequestedWebFeedSelection(_: TimelineViewController, webFeed: WebFeed) func timelineInvalidatedRestorationState(_: TimelineViewController) diff --git a/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift b/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift index 24ca1b742..d79525d30 100644 --- a/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift +++ b/Mac/Preferences/ExtensionPoints/ExtensionPointPreferencesViewController.swift @@ -12,7 +12,7 @@ import AuthenticationServices import OAuthSwift import Secrets -protocol ExtensionPointPreferencesEnabler: class { +protocol ExtensionPointPreferencesEnabler: AnyObject { func enable(_ extensionPointType: ExtensionPoint.Type) } diff --git a/Multiplatform/Shared/Sidebar/SidebarModel.swift b/Multiplatform/Shared/Sidebar/SidebarModel.swift index a7f87b52e..da8e36de7 100644 --- a/Multiplatform/Shared/Sidebar/SidebarModel.swift +++ b/Multiplatform/Shared/Sidebar/SidebarModel.swift @@ -12,7 +12,7 @@ import RSCore import Account import Articles -protocol SidebarModelDelegate: class { +protocol SidebarModelDelegate: AnyObject { func unreadCount(for: Feed) -> Int } diff --git a/Multiplatform/Shared/Timeline/TimelineModel.swift b/Multiplatform/Shared/Timeline/TimelineModel.swift index b96e5422a..a70cd6c69 100644 --- a/Multiplatform/Shared/Timeline/TimelineModel.swift +++ b/Multiplatform/Shared/Timeline/TimelineModel.swift @@ -16,7 +16,7 @@ import RSCore import Account import Articles -protocol TimelineModelDelegate: class { +protocol TimelineModelDelegate: AnyObject { var selectedFeedsPublisher: AnyPublisher<[Feed], Never>? { get } func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed) } diff --git a/Multiplatform/iOS/Article/WebViewController.swift b/Multiplatform/iOS/Article/WebViewController.swift index 18d8c44c4..5e5364d54 100644 --- a/Multiplatform/iOS/Article/WebViewController.swift +++ b/Multiplatform/iOS/Article/WebViewController.swift @@ -14,7 +14,7 @@ import Articles import SafariServices import MessageUI -protocol WebViewControllerDelegate: class { +protocol WebViewControllerDelegate: AnyObject { func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState) } diff --git a/Multiplatform/macOS/Article/WebViewController.swift b/Multiplatform/macOS/Article/WebViewController.swift index b415f71d2..e71c7ef1d 100644 --- a/Multiplatform/macOS/Article/WebViewController.swift +++ b/Multiplatform/macOS/Article/WebViewController.swift @@ -11,7 +11,7 @@ import Combine import RSCore import Articles -protocol WebViewControllerDelegate: class { +protocol WebViewControllerDelegate: AnyObject { func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState) } diff --git a/Shared/SmartFeeds/PseudoFeed.swift b/Shared/SmartFeeds/PseudoFeed.swift index 622e1b32f..2d49603f2 100644 --- a/Shared/SmartFeeds/PseudoFeed.swift +++ b/Shared/SmartFeeds/PseudoFeed.swift @@ -13,7 +13,7 @@ import Articles import Account import RSCore -protocol PseudoFeed: class, Feed, SmallIconProvider, PasteboardWriterOwner { +protocol PseudoFeed: AnyObject, Feed, SmallIconProvider, PasteboardWriterOwner { } @@ -24,7 +24,7 @@ import Articles import Account import RSCore -protocol PseudoFeed: class, Feed, SmallIconProvider { +protocol PseudoFeed: AnyObject, Feed, SmallIconProvider { } diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift index 6024d02dd..e00898eb5 100644 --- a/iOS/Article/WebViewController.swift +++ b/iOS/Article/WebViewController.swift @@ -14,7 +14,7 @@ import Articles import SafariServices import MessageUI -protocol WebViewControllerDelegate: class { +protocol WebViewControllerDelegate: AnyObject { func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState) } diff --git a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift index 8bb30a720..4cb6f90e6 100644 --- a/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift +++ b/iOS/MasterFeed/Cell/MasterFeedTableViewCell.swift @@ -11,7 +11,7 @@ import RSCore import Account import RSTree -protocol MasterFeedTableViewCellDelegate: class { +protocol MasterFeedTableViewCellDelegate: AnyObject { func masterFeedTableViewCellDisclosureDidToggle(_ sender: MasterFeedTableViewCell, expanding: Bool) } diff --git a/iOS/ShareExtension/ShareFolderPickerController.swift b/iOS/ShareExtension/ShareFolderPickerController.swift index d70fcf332..3d957974b 100644 --- a/iOS/ShareExtension/ShareFolderPickerController.swift +++ b/iOS/ShareExtension/ShareFolderPickerController.swift @@ -10,7 +10,7 @@ import UIKit import Account import RSCore -protocol ShareFolderPickerControllerDelegate: class { +protocol ShareFolderPickerControllerDelegate: AnyObject { func shareFolderPickerDidSelect(_ container: ExtensionContainer) } From 207e30239e312332d82ba45ce1a3aea77593fca4 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Sun, 7 Feb 2021 20:22:45 +0800 Subject: [PATCH 02/17] tidies up add account sheets --- .../Accounts/AccountsAddCloudKit.xib | 8 ++-- Mac/Preferences/Accounts/AccountsAddLocal.xib | 16 ++++--- .../Accounts/AccountsFeedWrangler.xib | 29 ++++++------ Mac/Preferences/Accounts/AccountsFeedbin.xib | 31 ++++++------ Mac/Preferences/Accounts/AccountsNewsBlur.xib | 31 ++++++------ .../Accounts/AccountsReaderAPI.xib | 47 ++++++++++--------- .../Accounts/AddAccountsView.swift | 8 ++-- 7 files changed, 89 insertions(+), 81 deletions(-) diff --git a/Mac/Preferences/Accounts/AccountsAddCloudKit.xib b/Mac/Preferences/Accounts/AccountsAddCloudKit.xib index 2d43d8cb4..0e286f444 100644 --- a/Mac/Preferences/Accounts/AccountsAddCloudKit.xib +++ b/Mac/Preferences/Accounts/AccountsAddCloudKit.xib @@ -1,8 +1,8 @@ - + - + @@ -18,7 +18,7 @@ - + @@ -92,7 +92,7 @@ Gw - + diff --git a/Mac/Preferences/Accounts/AccountsAddLocal.xib b/Mac/Preferences/Accounts/AccountsAddLocal.xib index 36e5232cf..35a7d35cf 100644 --- a/Mac/Preferences/Accounts/AccountsAddLocal.xib +++ b/Mac/Preferences/Accounts/AccountsAddLocal.xib @@ -1,8 +1,8 @@ - + - + @@ -19,7 +19,7 @@ - + @@ -31,7 +31,10 @@ - - - - + @@ -197,20 +197,21 @@ Gw - - + + - + + @@ -225,7 +226,7 @@ Gw - + diff --git a/Mac/Preferences/Accounts/AccountsNewsBlur.xib b/Mac/Preferences/Accounts/AccountsNewsBlur.xib index b6133daa2..c098cffc3 100644 --- a/Mac/Preferences/Accounts/AccountsNewsBlur.xib +++ b/Mac/Preferences/Accounts/AccountsNewsBlur.xib @@ -1,8 +1,8 @@ - + - + @@ -26,7 +26,7 @@ - + @@ -97,8 +97,11 @@ - @@ -224,7 +225,7 @@ Gw - + diff --git a/Mac/Preferences/Accounts/AccountsReaderAPI.xib b/Mac/Preferences/Accounts/AccountsReaderAPI.xib index 57c7be46c..46b81d5b3 100644 --- a/Mac/Preferences/Accounts/AccountsReaderAPI.xib +++ b/Mac/Preferences/Accounts/AccountsReaderAPI.xib @@ -1,8 +1,8 @@ - + - + @@ -29,17 +29,17 @@ - - + + - + - + - + @@ -125,8 +125,11 @@ - + @@ -208,10 +211,7 @@ Gw - - - - + @@ -232,15 +232,16 @@ Gw - + + - - + - + + @@ -250,7 +251,7 @@ Gw - + diff --git a/Mac/Preferences/Accounts/AddAccountsView.swift b/Mac/Preferences/Accounts/AddAccountsView.swift index eedc1131c..1eefa3d4b 100644 --- a/Mac/Preferences/Accounts/AddAccountsView.swift +++ b/Mac/Preferences/Accounts/AddAccountsView.swift @@ -101,7 +101,7 @@ struct AddAccountsView: View { parent?.dismiss(nil) }, label: { Text("Cancel") - .frame(width: 80) + .frame(width: 76) }) .help("Cancel") .keyboardShortcut(.cancelAction) @@ -111,7 +111,7 @@ struct AddAccountsView: View { parent?.dismiss(nil) }, label: { Text("Cancel") - .frame(width: 80) + .frame(width: 76) }) .accessibility(label: Text("Add Account")) } @@ -121,7 +121,7 @@ struct AddAccountsView: View { parent?.dismiss(nil) }, label: { Text("Continue") - .frame(width: 80) + .frame(width: 76) }) .help("Add Account") .keyboardShortcut(.defaultAction) @@ -132,7 +132,7 @@ struct AddAccountsView: View { parent?.dismiss(nil) }, label: { Text("Continue") - .frame(width: 80) + .frame(width: 76) }) } } From cf73fb5bd3cc2070b89724d94c37c38a69c04704 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 7 Feb 2021 17:46:24 -0600 Subject: [PATCH 03/17] Fix bug that was removing unread notifications by checking the status. Fixes #2786 --- Shared/UserNotifications/UserNotificationManager.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Shared/UserNotifications/UserNotificationManager.swift b/Shared/UserNotifications/UserNotificationManager.swift index edb0d07d0..d8ed11eeb 100644 --- a/Shared/UserNotifications/UserNotificationManager.swift +++ b/Shared/UserNotifications/UserNotificationManager.swift @@ -33,10 +33,10 @@ final class UserNotificationManager: NSObject { } @objc func statusesDidChange(_ note: Notification) { - guard let articleIDs = note.userInfo?[Account.UserInfoKey.articleIDs] as? Set, !articleIDs.isEmpty else { + guard let statuses = note.userInfo?[Account.UserInfoKey.statuses] as? Set, !statuses.isEmpty else { return } - let identifiers = articleIDs.map { "articleID:\($0)" } + let identifiers = statuses.filter({ $0.read }).map { "articleID:\($0.articleID)" } UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: identifiers) } From 91ce7ff189618d181b0b76a41ad4afc9149bdf0f Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 7 Feb 2021 20:26:30 -0600 Subject: [PATCH 04/17] Update to the latest RSCore --- Account/Package.swift | 2 +- .../Account/CloudKit/CloudKitAccountZone.swift | 13 ++++++------- .../Account/CloudKit/CloudKitArticlesZone.swift | 17 ++++++++--------- Articles/Package.swift | 2 +- ArticlesDatabase/Package.swift | 2 +- NetNewsWire.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 ++-- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Account/Package.swift b/Account/Package.swift index 8a53ae40e..2f556c766 100644 --- a/Account/Package.swift +++ b/Account/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["Account"]), ], dependencies: [ - .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0-beta1")), + .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0")), .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0-beta1")), .package(url: "https://github.com/Ranchero-Software/RSParser.git", .upToNextMajor(from: "2.0.0-beta1")), .package(url: "https://github.com/Ranchero-Software/RSWeb.git", .upToNextMajor(from: "1.0.0-beta1")), diff --git a/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift b/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift index e0a45e0cb..6152f41ac 100644 --- a/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift +++ b/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift @@ -21,9 +21,7 @@ enum CloudKitAccountZoneError: LocalizedError { } final class CloudKitAccountZone: CloudKitZone { - static var zoneID: CKRecordZone.ID { - return CKRecordZone.ID(zoneName: "Account", ownerName: CKCurrentUserDefaultName) - } + var zoneID: CKRecordZone.ID var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") @@ -53,6 +51,7 @@ final class CloudKitAccountZone: CloudKitZone { init(container: CKContainer) { self.container = container self.database = container.privateCloudDatabase + self.zoneID = CKRecordZone.ID(zoneName: "Account", ownerName: CKCurrentUserDefaultName) } func importOPML(rootExternalID: String, items: [RSOPMLItem], completion: @escaping (Result) -> Void) { @@ -91,7 +90,7 @@ final class CloudKitAccountZone: CloudKitZone { /// Persist a web feed record to iCloud and return the external key func createWebFeed(url: String, name: String?, editedName: String?, homePageURL: String?, container: Container, completion: @escaping (Result) -> Void) { - let recordID = CKRecord.ID(recordName: url.md5String, zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: url.md5String, zoneID: zoneID) let record = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: recordID) record[CloudKitWebFeed.Fields.url] = url record[CloudKitWebFeed.Fields.name] = name @@ -125,7 +124,7 @@ final class CloudKitAccountZone: CloudKitZone { return } - let recordID = CKRecord.ID(recordName: externalID, zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: externalID, zoneID: zoneID) let record = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: recordID) record[CloudKitWebFeed.Fields.editedName] = editedName @@ -252,7 +251,7 @@ final class CloudKitAccountZone: CloudKitZone { let predicate = NSPredicate(format: "isAccount = \"1\"") let ckQuery = CKQuery(recordType: CloudKitContainer.recordType, predicate: predicate) - database?.perform(ckQuery, inZoneWith: Self.zoneID) { [weak self] records, error in + database?.perform(ckQuery, inZoneWith: zoneID) { [weak self] records, error in guard let self = self else { return } switch CloudKitZoneResult.resolve(error) { @@ -296,7 +295,7 @@ final class CloudKitAccountZone: CloudKitZone { return } - let recordID = CKRecord.ID(recordName: externalID, zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: externalID, zoneID: zoneID) let record = CKRecord(recordType: CloudKitContainer.recordType, recordID: recordID) record[CloudKitContainer.Fields.name] = name diff --git a/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift b/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift index f085654ab..815303fdb 100644 --- a/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift +++ b/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift @@ -17,9 +17,7 @@ import SyncDatabase final class CloudKitArticlesZone: CloudKitZone { - static var zoneID: CKRecordZone.ID { - return CKRecordZone.ID(zoneName: "Articles", ownerName: CKCurrentUserDefaultName) - } + var zoneID: CKRecordZone.ID var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit") @@ -58,6 +56,7 @@ final class CloudKitArticlesZone: CloudKitZone { init(container: CKContainer) { self.container = container self.database = container.privateCloudDatabase + self.zoneID = CKRecordZone.ID(zoneName: "Articles", ownerName: CKCurrentUserDefaultName) } func refreshArticles(completion: @escaping ((Result) -> Void)) { @@ -124,10 +123,10 @@ final class CloudKitArticlesZone: CloudKitZone { newRecords.append(self.makeStatusRecord(statusUpdate)) newRecords.append(self.makeArticleRecord(statusUpdate.article!)) case .delete: - deleteRecordIDs.append(CKRecord.ID(recordName: self.statusID(statusUpdate.articleID), zoneID: Self.zoneID)) + deleteRecordIDs.append(CKRecord.ID(recordName: self.statusID(statusUpdate.articleID), zoneID: zoneID)) case .statusOnly: modifyRecords.append(self.makeStatusRecord(statusUpdate)) - deleteRecordIDs.append(CKRecord.ID(recordName: self.articleID(statusUpdate.articleID), zoneID: Self.zoneID)) + deleteRecordIDs.append(CKRecord.ID(recordName: self.articleID(statusUpdate.articleID), zoneID: zoneID)) } } @@ -176,7 +175,7 @@ private extension CloudKitArticlesZone { } func makeStatusRecord(_ article: Article) -> CKRecord { - let recordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID) let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID) if let webFeedExternalID = article.webFeed?.externalID { record[CloudKitArticleStatus.Fields.webFeedExternalID] = webFeedExternalID @@ -187,7 +186,7 @@ private extension CloudKitArticlesZone { } func makeStatusRecord(_ statusUpdate: CloudKitArticleStatusUpdate) -> CKRecord { - let recordID = CKRecord.ID(recordName: statusID(statusUpdate.articleID), zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: statusID(statusUpdate.articleID), zoneID: zoneID) let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID) if let webFeedExternalID = statusUpdate.article?.webFeed?.externalID { @@ -201,10 +200,10 @@ private extension CloudKitArticlesZone { } func makeArticleRecord(_ article: Article) -> CKRecord { - let recordID = CKRecord.ID(recordName: articleID(article.articleID), zoneID: Self.zoneID) + let recordID = CKRecord.ID(recordName: articleID(article.articleID), zoneID: zoneID) let record = CKRecord(recordType: CloudKitArticle.recordType, recordID: recordID) - let articleStatusRecordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: Self.zoneID) + let articleStatusRecordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID) record[CloudKitArticle.Fields.articleStatus] = CKRecord.Reference(recordID: articleStatusRecordID, action: .deleteSelf) record[CloudKitArticle.Fields.webFeedURL] = article.webFeed?.url record[CloudKitArticle.Fields.uniqueID] = article.uniqueID diff --git a/Articles/Package.swift b/Articles/Package.swift index 9a996999d..d5353dc79 100644 --- a/Articles/Package.swift +++ b/Articles/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["Articles"]), ], dependencies: [ - .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0-beta1")), + .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0")), ], targets: [ .target( diff --git a/ArticlesDatabase/Package.swift b/ArticlesDatabase/Package.swift index a8d1df496..200fd6137 100644 --- a/ArticlesDatabase/Package.swift +++ b/ArticlesDatabase/Package.swift @@ -13,7 +13,7 @@ let package = Package( targets: ["ArticlesDatabase"]), ], dependencies: [ - .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0-beta1")), + .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0")), .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0-beta1")), .package(url: "https://github.com/Ranchero-Software/RSParser.git", .upToNextMajor(from: "2.0.0-beta1")), .package(url: "../Articles", .upToNextMajor(from: "1.0.0")), diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index e262a739b..f33474265 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -6104,7 +6104,7 @@ repositoryURL = "https://github.com/Ranchero-Software/RSCore.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = "1.0.0-beta1"; + minimumVersion = 1.0.0; }; }; 510ECA4024D1DCD0001C31A6 /* XCRemoteSwiftPackageReference "RSTree" */ = { diff --git a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 3f40017d1..57fee8157 100644 --- a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSCore.git", "state": { "branch": null, - "revision": "dce76a4070ed24f148bb1673c308962dbdbf01ef", - "version": "1.0.0-beta9" + "revision": "3fb97de5901d10e6afede307a31957f7579042e5", + "version": "1.0.0" } }, { From 9b35e4a6b1727460990eb00cc99ecc008814c22d Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 8 Feb 2021 13:08:02 -0600 Subject: [PATCH 05/17] Update to the latest RSCore --- Account/Sources/Account/CloudKit/CloudKitAccountZone.swift | 1 + Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift | 1 + .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift b/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift index 6152f41ac..64c99794f 100644 --- a/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift +++ b/Account/Sources/Account/CloudKit/CloudKitAccountZone.swift @@ -52,6 +52,7 @@ final class CloudKitAccountZone: CloudKitZone { self.container = container self.database = container.privateCloudDatabase self.zoneID = CKRecordZone.ID(zoneName: "Account", ownerName: CKCurrentUserDefaultName) + migrateChangeToken() } func importOPML(rootExternalID: String, items: [RSOPMLItem], completion: @escaping (Result) -> Void) { diff --git a/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift b/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift index 815303fdb..095ddcdea 100644 --- a/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift +++ b/Account/Sources/Account/CloudKit/CloudKitArticlesZone.swift @@ -57,6 +57,7 @@ final class CloudKitArticlesZone: CloudKitZone { self.container = container self.database = container.privateCloudDatabase self.zoneID = CKRecordZone.ID(zoneName: "Articles", ownerName: CKCurrentUserDefaultName) + migrateChangeToken() } func refreshArticles(completion: @escaping ((Result) -> Void)) { diff --git a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 57fee8157..1d7ad5d1a 100644 --- a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSCore.git", "state": { "branch": null, - "revision": "3fb97de5901d10e6afede307a31957f7579042e5", - "version": "1.0.0" + "revision": "55295e826ac0249ac0c271e83c4489313b350a7c", + "version": "1.0.1" } }, { From 5636ca0a015d9e1cd636fabac62929a45a64e489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Oliveira?= Date: Tue, 9 Feb 2021 14:13:24 +0100 Subject: [PATCH 06/17] Add authors names to article search index Add authors to search table; Add authors names to ArticleSearchInfo and modified code to retrieve it correctly. Needs some cleaning up before being ready to merge. --- .../ArticlesDatabase/ArticlesDatabase.swift | 2 +- .../ArticlesDatabase/ArticlesTable.swift | 21 +++++++++++++++++-- .../ArticlesDatabase/SearchTable.swift | 8 ++++--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift index ddb576ba9..e2a62b8d0 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift @@ -332,7 +332,7 @@ private extension ArticlesDatabase { CREATE INDEX if not EXISTS statuses_starred_index on statuses (starred); - CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body); + CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body, authors); CREATE TRIGGER if not EXISTS articles_after_delete_trigger_delete_search_text after delete on articles begin delete from search where rowid = OLD.searchRowID; end; """ diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift index 61c2845ef..0f9533c74 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift @@ -149,7 +149,23 @@ final class ArticlesTable: DatabaseTable { func fetchArticleSearchInfos(_ articleIDs: Set, in database: FMDatabase) -> Set? { let parameters = articleIDs.map { $0 as AnyObject } let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))! - let sql = "select articleID, title, contentHTML, contentText, summary, searchRowID from articles where articleID in \(placeholders);"; + // Aggregating authors names in SQL using a subquery with GROUP_CONCAT + let sql = """ + SELECT + art.articleID, + art.title, + art.contentHTML, + art.contentText, + art.summary, + art.searchRowID, + (SELECT GROUP_CONCAT(name SEPARATOR ' ') + FROM authorLookup as autL + JOIN authors as aut ON autL.authorID = aut.authorID + WHERE art.articleID = autL.articleID + GROUP BY autl.articleID) as authors + FROM articles as art + WHERE articleID in \(placeholders); + """; if let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) { return resultSet.mapToSet { (row) -> ArticleSearchInfo? in @@ -158,6 +174,7 @@ final class ArticlesTable: DatabaseTable { let contentHTML = row.string(forColumn: DatabaseKey.contentHTML) let contentText = row.string(forColumn: DatabaseKey.contentText) let summary = row.string(forColumn: DatabaseKey.summary) + let authors = row.string(forColumn: DatabaseKey.authors) let searchRowIDObject = row.object(forColumnName: DatabaseKey.searchRowID) var searchRowID: Int? = nil @@ -165,7 +182,7 @@ final class ArticlesTable: DatabaseTable { searchRowID = Int(row.longLongInt(forColumn: DatabaseKey.searchRowID)) } - return ArticleSearchInfo(articleID: articleID, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, searchRowID: searchRowID) + return ArticleSearchInfo(articleID: articleID, title: title, authorsNames: authors, contentHTML: contentHTML, contentText: contentText, summary: summary, searchRowID: searchRowID) } } return nil diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift index 9f97a6d69..788ac6418 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift @@ -17,6 +17,7 @@ final class ArticleSearchInfo: Hashable { let articleID: String let title: String? + let authorsNames: String? let contentHTML: String? let contentText: String? let summary: String? @@ -37,9 +38,10 @@ final class ArticleSearchInfo: Hashable { return s.strippingHTML().collapsingWhitespace }() - init(articleID: String, title: String?, contentHTML: String?, contentText: String?, summary: String?, searchRowID: Int?) { + init(articleID: String, title: String?, authorsNames: String?, contentHTML: String?, contentText: String?, summary: String?, searchRowID: Int?) { self.articleID = articleID self.title = title + self.authorsNames = authorsNames self.contentHTML = contentHTML self.contentText = contentText self.summary = summary @@ -47,7 +49,7 @@ final class ArticleSearchInfo: Hashable { } convenience init(article: Article) { - self.init(articleID: article.articleID, title: article.title, contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) + self.init(articleID: article.articleID, title: article.title, authorsNames: article.authors?.map({ $0.name }).reduce("", { $0.appending("").appending($1 ?? "") }), contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) } // MARK: Hashable @@ -127,7 +129,7 @@ private extension SearchTable { } func insert(_ article: ArticleSearchInfo, _ database: FMDatabase) -> Int { - let rowDictionary: DatabaseDictionary = [DatabaseKey.body: article.bodyForIndex, DatabaseKey.title: article.title ?? ""] + let rowDictionary: DatabaseDictionary = [DatabaseKey.body: article.bodyForIndex, DatabaseKey.title: article.title ?? "", DatabaseKey.authors: article.authorsNames ?? ""] insertRow(rowDictionary, insertType: .normal, in: database) return Int(database.lastInsertRowId()) } From cf1be330447a5f20202cc70e32bcc52677056dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Oliveira?= Date: Wed, 10 Feb 2021 19:46:16 +0100 Subject: [PATCH 07/17] Add code to migrate database model Add code to migrate database model when `authors` column is not present in the index This recreates `search` table and resets the articles index Added missing or code in error --- .../Sources/ArticlesDatabase/ArticlesDatabase.swift | 5 +++++ .../Sources/ArticlesDatabase/ArticlesTable.swift | 4 ++-- ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift index e2a62b8d0..800cb0f4c 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift @@ -63,6 +63,7 @@ public final class ArticlesDatabase { } private let articlesTable: ArticlesTable + private let searchTable: SearchTable private let queue: DatabaseQueue private let operationQueue = MainThreadOperationQueue() private let retentionStyle: RetentionStyle @@ -71,6 +72,7 @@ public final class ArticlesDatabase { let queue = DatabaseQueue(databasePath: databaseFilePath) self.queue = queue self.articlesTable = ArticlesTable(name: DatabaseTableName.articles, accountID: accountID, queue: queue, retentionStyle: retentionStyle) + self.searchTable = SearchTable(queue: queue, articlesTable: self.articlesTable) self.retentionStyle = retentionStyle try! queue.runCreateStatements(ArticlesDatabase.tableCreationStatements) @@ -81,6 +83,9 @@ public final class ArticlesDatabase { } database.executeStatements("CREATE INDEX if not EXISTS articles_searchRowID on articles(searchRowID);") database.executeStatements("DROP TABLE if EXISTS tags;DROP INDEX if EXISTS tags_tagName_index;DROP INDEX if EXISTS articles_feedID_index;DROP INDEX if EXISTS statuses_read_index;DROP TABLE if EXISTS attachments;DROP TABLE if EXISTS attachmentsLookup;") + if !self.searchTable.containsColumn("authors", in: database) { + database.executeStatements("DROP TABLE if EXISTS search;CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body, authors);UPDATE articles SET searchRowID = null;") + } } DispatchQueue.main.async { diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift index 0f9533c74..2a925612e 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift @@ -158,8 +158,8 @@ final class ArticlesTable: DatabaseTable { art.contentText, art.summary, art.searchRowID, - (SELECT GROUP_CONCAT(name SEPARATOR ' ') - FROM authorLookup as autL + (SELECT GROUP_CONCAT(name, ' ') + FROM authorsLookup as autL JOIN authors as aut ON autL.authorID = aut.authorID WHERE art.articleID = autL.articleID GROUP BY autl.articleID) as authors diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift index 788ac6418..acb6a2e64 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift @@ -61,7 +61,7 @@ final class ArticleSearchInfo: Hashable { // MARK: Equatable static func == (lhs: ArticleSearchInfo, rhs: ArticleSearchInfo) -> Bool { - return lhs.articleID == rhs.articleID && lhs.title == rhs.title && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.summary == rhs.summary && lhs.searchRowID == rhs.searchRowID + return lhs.articleID == rhs.articleID && lhs.title == rhs.title && lhs.authorsNames == rhs.authorsNames && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.summary == rhs.summary && lhs.searchRowID == rhs.searchRowID } } @@ -203,7 +203,7 @@ private extension SearchTable { return nil } let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(searchRowIDs.count))! - let sql = "select rowid, title, body from \(name) where rowid in \(placeholders);" + let sql = "select rowid, title, body, authors from \(name) where rowid in \(placeholders);" guard let resultSet = database.executeQuery(sql, withArgumentsIn: searchRowIDs) else { return nil } From 408e5d0ba092d1e992b0e68547df607c3bc9286e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Oliveira?= Date: Wed, 10 Feb 2021 20:25:21 +0100 Subject: [PATCH 08/17] Clean code - Extract statements to create `search` table so it can be reused during database migration - Rename `authors` as `authorsNames` for consistency - Extract code to improve readability --- .../ArticlesDatabase/ArticlesDatabase.swift | 14 ++++++++----- .../ArticlesDatabase/ArticlesTable.swift | 20 +++++++++---------- .../ArticlesDatabase/SearchTable.swift | 3 ++- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift index 800cb0f4c..fa01b7a3d 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift @@ -76,6 +76,7 @@ public final class ArticlesDatabase { self.retentionStyle = retentionStyle try! queue.runCreateStatements(ArticlesDatabase.tableCreationStatements) + try! queue.runCreateStatements(ArticlesDatabase.searchTableCreationStatements) queue.runInDatabase { databaseResult in let database = databaseResult.database! if !self.articlesTable.containsColumn("searchRowID", in: database) { @@ -84,7 +85,8 @@ public final class ArticlesDatabase { database.executeStatements("CREATE INDEX if not EXISTS articles_searchRowID on articles(searchRowID);") database.executeStatements("DROP TABLE if EXISTS tags;DROP INDEX if EXISTS tags_tagName_index;DROP INDEX if EXISTS articles_feedID_index;DROP INDEX if EXISTS statuses_read_index;DROP TABLE if EXISTS attachments;DROP TABLE if EXISTS attachmentsLookup;") if !self.searchTable.containsColumn("authors", in: database) { - database.executeStatements("DROP TABLE if EXISTS search;CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body, authors);UPDATE articles SET searchRowID = null;") + database.executeStatements("DROP TABLE if EXISTS search;UPDATE articles SET searchRowID = null;") + database.executeStatements(ArticlesDatabase.searchTableCreationStatements) } } @@ -336,12 +338,14 @@ private extension ArticlesDatabase { CREATE INDEX if not EXISTS articles_feedID_datePublished_articleID on articles (feedID, datePublished, articleID); CREATE INDEX if not EXISTS statuses_starred_index on statuses (starred); - - CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body, authors); - - CREATE TRIGGER if not EXISTS articles_after_delete_trigger_delete_search_text after delete on articles begin delete from search where rowid = OLD.searchRowID; end; """ + static let searchTableCreationStatements = """ + CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body, authors); + + CREATE TRIGGER if not EXISTS articles_after_delete_trigger_delete_search_text after delete on articles begin delete from search where rowid = OLD.searchRowID; end; + """ + func todayCutoffDate() -> Date { // 24 hours previous. This is used by the Today smart feed, which should not actually empty out at midnight. return Date(timeIntervalSinceNow: -(60 * 60 * 24)) // This does not need to be more precise. diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift index 2a925612e..099f626a1 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift @@ -145,12 +145,8 @@ final class ArticlesTable: DatabaseTable { } // MARK: - Fetching Articles for Indexer - - func fetchArticleSearchInfos(_ articleIDs: Set, in database: FMDatabase) -> Set? { - let parameters = articleIDs.map { $0 as AnyObject } - let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))! - // Aggregating authors names in SQL using a subquery with GROUP_CONCAT - let sql = """ + private func articleSearchInfosQuery(with placeholders: String) -> String { + return """ SELECT art.articleID, art.title, @@ -165,16 +161,20 @@ final class ArticlesTable: DatabaseTable { GROUP BY autl.articleID) as authors FROM articles as art WHERE articleID in \(placeholders); - """; + """ + } - if let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) { + func fetchArticleSearchInfos(_ articleIDs: Set, in database: FMDatabase) -> Set? { + let parameters = articleIDs.map { $0 as AnyObject } + let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))! + if let resultSet = database.executeQuery(self.articleSearchInfosQuery(with: placeholders), withArgumentsIn: parameters) { return resultSet.mapToSet { (row) -> ArticleSearchInfo? in let articleID = row.string(forColumn: DatabaseKey.articleID)! let title = row.string(forColumn: DatabaseKey.title) let contentHTML = row.string(forColumn: DatabaseKey.contentHTML) let contentText = row.string(forColumn: DatabaseKey.contentText) let summary = row.string(forColumn: DatabaseKey.summary) - let authors = row.string(forColumn: DatabaseKey.authors) + let authorsNames = row.string(forColumn: DatabaseKey.authors) let searchRowIDObject = row.object(forColumnName: DatabaseKey.searchRowID) var searchRowID: Int? = nil @@ -182,7 +182,7 @@ final class ArticlesTable: DatabaseTable { searchRowID = Int(row.longLongInt(forColumn: DatabaseKey.searchRowID)) } - return ArticleSearchInfo(articleID: articleID, title: title, authorsNames: authors, contentHTML: contentHTML, contentText: contentText, summary: summary, searchRowID: searchRowID) + return ArticleSearchInfo(articleID: articleID, title: title, authorsNames: authorsNames, contentHTML: contentHTML, contentText: contentText, summary: summary, searchRowID: searchRowID) } } return nil diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift index acb6a2e64..7aca4ae5a 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift @@ -49,7 +49,8 @@ final class ArticleSearchInfo: Hashable { } convenience init(article: Article) { - self.init(articleID: article.articleID, title: article.title, authorsNames: article.authors?.map({ $0.name }).reduce("", { $0.appending("").appending($1 ?? "") }), contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) + let authorsNames = article.authors?.map({ $0.name }).reduce("", { $0.appending("").appending($1 ?? "") }) + self.init(articleID: article.articleID, title: article.title, authorsNames: authorsNames, contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) } // MARK: Hashable From c063880c489ea19d614c26e504b9e44ac7f5bf77 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 10 Feb 2021 16:19:26 -0600 Subject: [PATCH 09/17] Upgraded to production releases of RS libraries --- Account/Package.swift | 6 +++--- ArticlesDatabase/Package.swift | 4 ++-- NetNewsWire.xcodeproj/project.pbxproj | 10 +++++----- .../xcshareddata/swiftpm/Package.resolved | 16 ++++++++-------- SyncDatabase/Package.swift | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Account/Package.swift b/Account/Package.swift index 2f556c766..3d49c12e0 100644 --- a/Account/Package.swift +++ b/Account/Package.swift @@ -12,9 +12,9 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0")), - .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0-beta1")), - .package(url: "https://github.com/Ranchero-Software/RSParser.git", .upToNextMajor(from: "2.0.0-beta1")), - .package(url: "https://github.com/Ranchero-Software/RSWeb.git", .upToNextMajor(from: "1.0.0-beta1")), + .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0")), + .package(url: "https://github.com/Ranchero-Software/RSParser.git", .upToNextMajor(from: "2.0.0")), + .package(url: "https://github.com/Ranchero-Software/RSWeb.git", .upToNextMajor(from: "1.0.0")), .package(url: "../Articles", .upToNextMajor(from: "1.0.0")), .package(url: "../ArticlesDatabase", .upToNextMajor(from: "1.0.0")), .package(url: "../Secrets", .upToNextMajor(from: "1.0.0")), diff --git a/ArticlesDatabase/Package.swift b/ArticlesDatabase/Package.swift index 200fd6137..6a9076bba 100644 --- a/ArticlesDatabase/Package.swift +++ b/ArticlesDatabase/Package.swift @@ -14,8 +14,8 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/Ranchero-Software/RSCore.git", .upToNextMajor(from: "1.0.0")), - .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0-beta1")), - .package(url: "https://github.com/Ranchero-Software/RSParser.git", .upToNextMajor(from: "2.0.0-beta1")), + .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0")), + .package(url: "https://github.com/Ranchero-Software/RSParser.git", .upToNextMajor(from: "2.0.0")), .package(url: "../Articles", .upToNextMajor(from: "1.0.0")), ], targets: [ diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index f33474265..55c04659d 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -6095,8 +6095,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/Ranchero-Software/Sparkle-Binary.git"; requirement = { - branch = main; - kind = branch; + kind = upToNextMajorVersion; + minimumVersion = 2.0.0; }; }; 5102AE4324D17E820050839C /* XCRemoteSwiftPackageReference "RSCore" */ = { @@ -6112,7 +6112,7 @@ repositoryURL = "https://github.com/Ranchero-Software/RSTree.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = "1.0.0-beta1"; + minimumVersion = 1.0.0; }; }; 51383A3024D1F90E0027E272 /* XCRemoteSwiftPackageReference "RSWeb" */ = { @@ -6136,7 +6136,7 @@ repositoryURL = "https://github.com/Ranchero-Software/RSDatabase.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = "1.0.0-beta1"; + minimumVersion = 1.0.0; }; }; 51B0DF2324D2C7FA000AD99E /* XCRemoteSwiftPackageReference "RSParser" */ = { @@ -6144,7 +6144,7 @@ repositoryURL = "https://github.com/Ranchero-Software/RSParser.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = "2.0.0-beta1"; + minimumVersion = 2.0.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1d7ad5d1a..0ca6b48cc 100644 --- a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -69,8 +69,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSDatabase.git", "state": { "branch": null, - "revision": "3aa706f3adfc0b798a2b69cf536461c39db4d269", - "version": "1.0.0-beta1" + "revision": "a6c5f1622320f745cc9a0a910d1bed1e2eaf15e3", + "version": "1.0.0" } }, { @@ -78,8 +78,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSParser.git", "state": { "branch": null, - "revision": "fd9b9c974d551a9c94d970da90a42571d234efd6", - "version": "2.0.0-beta4" + "revision": "a4467cb6ab32d67fa8b09fcef8b234c7f96b7f9c", + "version": "2.0.0" } }, { @@ -87,8 +87,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSTree.git", "state": { "branch": null, - "revision": "979ed0eb610b6d95dc7adcf4620bd44205f512a6", - "version": "1.0.0-beta1" + "revision": "9d051f42cfc4faa991fd79cdb32e4cc8c545e334", + "version": "1.0.0" } }, { @@ -104,9 +104,9 @@ "package": "RSSparkle", "repositoryURL": "https://github.com/Ranchero-Software/Sparkle-Binary.git", "state": { - "branch": "main", + "branch": null, "revision": "67cd26321bdf4e77954cf6de7d9e6a20544f2030", - "version": null + "version": "2.0.0" } }, { diff --git a/SyncDatabase/Package.swift b/SyncDatabase/Package.swift index b78824b86..ae85d01d1 100644 --- a/SyncDatabase/Package.swift +++ b/SyncDatabase/Package.swift @@ -11,7 +11,7 @@ let package = Package( targets: ["SyncDatabase"]), ], dependencies: [ - .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0-beta1")), + .package(url: "https://github.com/Ranchero-Software/RSDatabase.git", .upToNextMajor(from: "1.0.0")), .package(url: "../Articles", .upToNextMajor(from: "1.0.0")), ], targets: [ From 53b3046635b4312ef0042622ba0925891a2cd360 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 10 Feb 2021 16:54:28 -0600 Subject: [PATCH 10/17] Revert "Adds author name as a searchable field" --- .../ArticlesDatabase/ArticlesDatabase.swift | 17 +++---------- .../ArticlesDatabase/ArticlesTable.swift | 25 +++---------------- .../ArticlesDatabase/SearchTable.swift | 13 ++++------ 3 files changed, 13 insertions(+), 42 deletions(-) diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift index fa01b7a3d..ddb576ba9 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift @@ -63,7 +63,6 @@ public final class ArticlesDatabase { } private let articlesTable: ArticlesTable - private let searchTable: SearchTable private let queue: DatabaseQueue private let operationQueue = MainThreadOperationQueue() private let retentionStyle: RetentionStyle @@ -72,11 +71,9 @@ public final class ArticlesDatabase { let queue = DatabaseQueue(databasePath: databaseFilePath) self.queue = queue self.articlesTable = ArticlesTable(name: DatabaseTableName.articles, accountID: accountID, queue: queue, retentionStyle: retentionStyle) - self.searchTable = SearchTable(queue: queue, articlesTable: self.articlesTable) self.retentionStyle = retentionStyle try! queue.runCreateStatements(ArticlesDatabase.tableCreationStatements) - try! queue.runCreateStatements(ArticlesDatabase.searchTableCreationStatements) queue.runInDatabase { databaseResult in let database = databaseResult.database! if !self.articlesTable.containsColumn("searchRowID", in: database) { @@ -84,10 +81,6 @@ public final class ArticlesDatabase { } database.executeStatements("CREATE INDEX if not EXISTS articles_searchRowID on articles(searchRowID);") database.executeStatements("DROP TABLE if EXISTS tags;DROP INDEX if EXISTS tags_tagName_index;DROP INDEX if EXISTS articles_feedID_index;DROP INDEX if EXISTS statuses_read_index;DROP TABLE if EXISTS attachments;DROP TABLE if EXISTS attachmentsLookup;") - if !self.searchTable.containsColumn("authors", in: database) { - database.executeStatements("DROP TABLE if EXISTS search;UPDATE articles SET searchRowID = null;") - database.executeStatements(ArticlesDatabase.searchTableCreationStatements) - } } DispatchQueue.main.async { @@ -338,14 +331,12 @@ private extension ArticlesDatabase { CREATE INDEX if not EXISTS articles_feedID_datePublished_articleID on articles (feedID, datePublished, articleID); CREATE INDEX if not EXISTS statuses_starred_index on statuses (starred); + + CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body); + + CREATE TRIGGER if not EXISTS articles_after_delete_trigger_delete_search_text after delete on articles begin delete from search where rowid = OLD.searchRowID; end; """ - static let searchTableCreationStatements = """ - CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body, authors); - - CREATE TRIGGER if not EXISTS articles_after_delete_trigger_delete_search_text after delete on articles begin delete from search where rowid = OLD.searchRowID; end; - """ - func todayCutoffDate() -> Date { // 24 hours previous. This is used by the Today smart feed, which should not actually empty out at midnight. return Date(timeIntervalSinceNow: -(60 * 60 * 24)) // This does not need to be more precise. diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift index 099f626a1..61c2845ef 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift @@ -145,36 +145,19 @@ final class ArticlesTable: DatabaseTable { } // MARK: - Fetching Articles for Indexer - private func articleSearchInfosQuery(with placeholders: String) -> String { - return """ - SELECT - art.articleID, - art.title, - art.contentHTML, - art.contentText, - art.summary, - art.searchRowID, - (SELECT GROUP_CONCAT(name, ' ') - FROM authorsLookup as autL - JOIN authors as aut ON autL.authorID = aut.authorID - WHERE art.articleID = autL.articleID - GROUP BY autl.articleID) as authors - FROM articles as art - WHERE articleID in \(placeholders); - """ - } func fetchArticleSearchInfos(_ articleIDs: Set, in database: FMDatabase) -> Set? { let parameters = articleIDs.map { $0 as AnyObject } let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))! - if let resultSet = database.executeQuery(self.articleSearchInfosQuery(with: placeholders), withArgumentsIn: parameters) { + let sql = "select articleID, title, contentHTML, contentText, summary, searchRowID from articles where articleID in \(placeholders);"; + + if let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) { return resultSet.mapToSet { (row) -> ArticleSearchInfo? in let articleID = row.string(forColumn: DatabaseKey.articleID)! let title = row.string(forColumn: DatabaseKey.title) let contentHTML = row.string(forColumn: DatabaseKey.contentHTML) let contentText = row.string(forColumn: DatabaseKey.contentText) let summary = row.string(forColumn: DatabaseKey.summary) - let authorsNames = row.string(forColumn: DatabaseKey.authors) let searchRowIDObject = row.object(forColumnName: DatabaseKey.searchRowID) var searchRowID: Int? = nil @@ -182,7 +165,7 @@ final class ArticlesTable: DatabaseTable { searchRowID = Int(row.longLongInt(forColumn: DatabaseKey.searchRowID)) } - return ArticleSearchInfo(articleID: articleID, title: title, authorsNames: authorsNames, contentHTML: contentHTML, contentText: contentText, summary: summary, searchRowID: searchRowID) + return ArticleSearchInfo(articleID: articleID, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, searchRowID: searchRowID) } } return nil diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift index 7aca4ae5a..9f97a6d69 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift @@ -17,7 +17,6 @@ final class ArticleSearchInfo: Hashable { let articleID: String let title: String? - let authorsNames: String? let contentHTML: String? let contentText: String? let summary: String? @@ -38,10 +37,9 @@ final class ArticleSearchInfo: Hashable { return s.strippingHTML().collapsingWhitespace }() - init(articleID: String, title: String?, authorsNames: String?, contentHTML: String?, contentText: String?, summary: String?, searchRowID: Int?) { + init(articleID: String, title: String?, contentHTML: String?, contentText: String?, summary: String?, searchRowID: Int?) { self.articleID = articleID self.title = title - self.authorsNames = authorsNames self.contentHTML = contentHTML self.contentText = contentText self.summary = summary @@ -49,8 +47,7 @@ final class ArticleSearchInfo: Hashable { } convenience init(article: Article) { - let authorsNames = article.authors?.map({ $0.name }).reduce("", { $0.appending("").appending($1 ?? "") }) - self.init(articleID: article.articleID, title: article.title, authorsNames: authorsNames, contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) + self.init(articleID: article.articleID, title: article.title, contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) } // MARK: Hashable @@ -62,7 +59,7 @@ final class ArticleSearchInfo: Hashable { // MARK: Equatable static func == (lhs: ArticleSearchInfo, rhs: ArticleSearchInfo) -> Bool { - return lhs.articleID == rhs.articleID && lhs.title == rhs.title && lhs.authorsNames == rhs.authorsNames && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.summary == rhs.summary && lhs.searchRowID == rhs.searchRowID + return lhs.articleID == rhs.articleID && lhs.title == rhs.title && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.summary == rhs.summary && lhs.searchRowID == rhs.searchRowID } } @@ -130,7 +127,7 @@ private extension SearchTable { } func insert(_ article: ArticleSearchInfo, _ database: FMDatabase) -> Int { - let rowDictionary: DatabaseDictionary = [DatabaseKey.body: article.bodyForIndex, DatabaseKey.title: article.title ?? "", DatabaseKey.authors: article.authorsNames ?? ""] + let rowDictionary: DatabaseDictionary = [DatabaseKey.body: article.bodyForIndex, DatabaseKey.title: article.title ?? ""] insertRow(rowDictionary, insertType: .normal, in: database) return Int(database.lastInsertRowId()) } @@ -204,7 +201,7 @@ private extension SearchTable { return nil } let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(searchRowIDs.count))! - let sql = "select rowid, title, body, authors from \(name) where rowid in \(placeholders);" + let sql = "select rowid, title, body from \(name) where rowid in \(placeholders);" guard let resultSet = database.executeQuery(sql, withArgumentsIn: searchRowIDs) else { return nil } From 6903db357a9573a92fc726b2297cdb20a91fb9f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Oliveira?= Date: Thu, 11 Feb 2021 00:26:26 +0100 Subject: [PATCH 11/17] Update ArticlesDatabase.swift Delete code that forced rebuilding the search index --- .../Sources/ArticlesDatabase/ArticlesDatabase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift index fa01b7a3d..f0243c47f 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift @@ -85,7 +85,7 @@ public final class ArticlesDatabase { database.executeStatements("CREATE INDEX if not EXISTS articles_searchRowID on articles(searchRowID);") database.executeStatements("DROP TABLE if EXISTS tags;DROP INDEX if EXISTS tags_tagName_index;DROP INDEX if EXISTS articles_feedID_index;DROP INDEX if EXISTS statuses_read_index;DROP TABLE if EXISTS attachments;DROP TABLE if EXISTS attachmentsLookup;") if !self.searchTable.containsColumn("authors", in: database) { - database.executeStatements("DROP TABLE if EXISTS search;UPDATE articles SET searchRowID = null;") + database.executeStatements("DROP TABLE if EXISTS search;") database.executeStatements(ArticlesDatabase.searchTableCreationStatements) } } From b4e150044bfafbb809941b641003cde57cc176e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Oliveira?= Date: Thu, 11 Feb 2021 01:31:36 +0100 Subject: [PATCH 12/17] Add author information to indexed body - Restore old code for database model - Modified `bodyForIndex` function to return `authorsNames` in it --- .../ArticlesDatabase/ArticlesDatabase.swift | 17 ++++------------- .../Sources/ArticlesDatabase/SearchTable.swift | 14 ++++++++++---- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift index f0243c47f..ddb576ba9 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesDatabase.swift @@ -63,7 +63,6 @@ public final class ArticlesDatabase { } private let articlesTable: ArticlesTable - private let searchTable: SearchTable private let queue: DatabaseQueue private let operationQueue = MainThreadOperationQueue() private let retentionStyle: RetentionStyle @@ -72,11 +71,9 @@ public final class ArticlesDatabase { let queue = DatabaseQueue(databasePath: databaseFilePath) self.queue = queue self.articlesTable = ArticlesTable(name: DatabaseTableName.articles, accountID: accountID, queue: queue, retentionStyle: retentionStyle) - self.searchTable = SearchTable(queue: queue, articlesTable: self.articlesTable) self.retentionStyle = retentionStyle try! queue.runCreateStatements(ArticlesDatabase.tableCreationStatements) - try! queue.runCreateStatements(ArticlesDatabase.searchTableCreationStatements) queue.runInDatabase { databaseResult in let database = databaseResult.database! if !self.articlesTable.containsColumn("searchRowID", in: database) { @@ -84,10 +81,6 @@ public final class ArticlesDatabase { } database.executeStatements("CREATE INDEX if not EXISTS articles_searchRowID on articles(searchRowID);") database.executeStatements("DROP TABLE if EXISTS tags;DROP INDEX if EXISTS tags_tagName_index;DROP INDEX if EXISTS articles_feedID_index;DROP INDEX if EXISTS statuses_read_index;DROP TABLE if EXISTS attachments;DROP TABLE if EXISTS attachmentsLookup;") - if !self.searchTable.containsColumn("authors", in: database) { - database.executeStatements("DROP TABLE if EXISTS search;") - database.executeStatements(ArticlesDatabase.searchTableCreationStatements) - } } DispatchQueue.main.async { @@ -338,14 +331,12 @@ private extension ArticlesDatabase { CREATE INDEX if not EXISTS articles_feedID_datePublished_articleID on articles (feedID, datePublished, articleID); CREATE INDEX if not EXISTS statuses_starred_index on statuses (starred); + + CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body); + + CREATE TRIGGER if not EXISTS articles_after_delete_trigger_delete_search_text after delete on articles begin delete from search where rowid = OLD.searchRowID; end; """ - static let searchTableCreationStatements = """ - CREATE VIRTUAL TABLE if not EXISTS search using fts4(title, body, authors); - - CREATE TRIGGER if not EXISTS articles_after_delete_trigger_delete_search_text after delete on articles begin delete from search where rowid = OLD.searchRowID; end; - """ - func todayCutoffDate() -> Date { // 24 hours previous. This is used by the Today smart feed, which should not actually empty out at midnight. return Date(timeIntervalSinceNow: -(60 * 60 * 24)) // This does not need to be more precise. diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift index 7aca4ae5a..100eabd3f 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift @@ -35,7 +35,13 @@ final class ArticleSearchInfo: Hashable { lazy var bodyForIndex: String = { let s = preferredText.rsparser_stringByDecodingHTMLEntities() - return s.strippingHTML().collapsingWhitespace + let sanitizedBody = s.strippingHTML().collapsingWhitespace + + if let authorsNames = authorsNames { + return sanitizedBody.appending(" \(authorsNames)") + } else { + return sanitizedBody + } }() init(articleID: String, title: String?, authorsNames: String?, contentHTML: String?, contentText: String?, summary: String?, searchRowID: Int?) { @@ -49,7 +55,7 @@ final class ArticleSearchInfo: Hashable { } convenience init(article: Article) { - let authorsNames = article.authors?.map({ $0.name }).reduce("", { $0.appending("").appending($1 ?? "") }) + let authorsNames = article.authors?.map({ $0.name }).reduce("", { $0.appending("").appending($1 ?? "") }).collapsingWhitespace self.init(articleID: article.articleID, title: article.title, authorsNames: authorsNames, contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) } @@ -130,7 +136,7 @@ private extension SearchTable { } func insert(_ article: ArticleSearchInfo, _ database: FMDatabase) -> Int { - let rowDictionary: DatabaseDictionary = [DatabaseKey.body: article.bodyForIndex, DatabaseKey.title: article.title ?? "", DatabaseKey.authors: article.authorsNames ?? ""] + let rowDictionary: DatabaseDictionary = [DatabaseKey.body: article.bodyForIndex, DatabaseKey.title: article.title ?? ""] insertRow(rowDictionary, insertType: .normal, in: database) return Int(database.lastInsertRowId()) } @@ -204,7 +210,7 @@ private extension SearchTable { return nil } let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(searchRowIDs.count))! - let sql = "select rowid, title, body, authors from \(name) where rowid in \(placeholders);" + let sql = "select rowid, title, body from \(name) where rowid in \(placeholders);" guard let resultSet = database.executeQuery(sql, withArgumentsIn: searchRowIDs) else { return nil } From ddf9f4114519bdc2ff440ce33c02c1fb06568ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A1bio=20Oliveira?= Date: Thu, 11 Feb 2021 22:59:45 +0100 Subject: [PATCH 13/17] Re add code and clean up - Re add code reverted earlier - Clean up code that builds `authorsNames` based on model objects - Applied the right indentation --- .../ArticlesDatabase/ArticlesTable.swift | 25 +++++++++++++--- .../ArticlesDatabase/SearchTable.swift | 29 ++++++++++++------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift index 61c2845ef..c495f08aa 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/ArticlesTable.swift @@ -145,19 +145,36 @@ final class ArticlesTable: DatabaseTable { } // MARK: - Fetching Articles for Indexer + private func articleSearchInfosQuery(with placeholders: String) -> String { + return """ + SELECT + art.articleID, + art.title, + art.contentHTML, + art.contentText, + art.summary, + art.searchRowID, + (SELECT GROUP_CONCAT(name, ' ') + FROM authorsLookup as autL + JOIN authors as aut ON autL.authorID = aut.authorID + WHERE art.articleID = autL.articleID + GROUP BY autl.articleID) as authors + FROM articles as art + WHERE articleID in \(placeholders); + """ + } func fetchArticleSearchInfos(_ articleIDs: Set, in database: FMDatabase) -> Set? { let parameters = articleIDs.map { $0 as AnyObject } let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))! - let sql = "select articleID, title, contentHTML, contentText, summary, searchRowID from articles where articleID in \(placeholders);"; - - if let resultSet = database.executeQuery(sql, withArgumentsIn: parameters) { + if let resultSet = database.executeQuery(self.articleSearchInfosQuery(with: placeholders), withArgumentsIn: parameters) { return resultSet.mapToSet { (row) -> ArticleSearchInfo? in let articleID = row.string(forColumn: DatabaseKey.articleID)! let title = row.string(forColumn: DatabaseKey.title) let contentHTML = row.string(forColumn: DatabaseKey.contentHTML) let contentText = row.string(forColumn: DatabaseKey.contentText) let summary = row.string(forColumn: DatabaseKey.summary) + let authorsNames = row.string(forColumn: DatabaseKey.authors) let searchRowIDObject = row.object(forColumnName: DatabaseKey.searchRowID) var searchRowID: Int? = nil @@ -165,7 +182,7 @@ final class ArticlesTable: DatabaseTable { searchRowID = Int(row.longLongInt(forColumn: DatabaseKey.searchRowID)) } - return ArticleSearchInfo(articleID: articleID, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, searchRowID: searchRowID) + return ArticleSearchInfo(articleID: articleID, title: title, contentHTML: contentHTML, contentText: contentText, summary: summary, authorsNames: authorsNames, searchRowID: searchRowID) } } return nil diff --git a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift index 15bcb0cdd..ed08439f4 100644 --- a/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift +++ b/ArticlesDatabase/Sources/ArticlesDatabase/SearchTable.swift @@ -20,6 +20,7 @@ final class ArticleSearchInfo: Hashable { let contentHTML: String? let contentText: String? let summary: String? + let authorsNames: String? let searchRowID: Int? var preferredText: String { @@ -34,18 +35,19 @@ final class ArticleSearchInfo: Hashable { lazy var bodyForIndex: String = { let s = preferredText.rsparser_stringByDecodingHTMLEntities() - let sanitizedBody = s.strippingHTML().collapsingWhitespace - - if let authorsNames = authorsNames { - return sanitizedBody.appending(" \(authorsNames)") - } else { - return sanitizedBody - } + let sanitizedBody = s.strippingHTML().collapsingWhitespace + + if let authorsNames = authorsNames { + return sanitizedBody.appending(" \(authorsNames)") + } else { + return sanitizedBody + } }() - init(articleID: String, title: String?, contentHTML: String?, contentText: String?, summary: String?, searchRowID: Int?) { + init(articleID: String, title: String?, contentHTML: String?, contentText: String?, summary: String?, authorsNames: String?, searchRowID: Int?) { self.articleID = articleID self.title = title + self.authorsNames = authorsNames self.contentHTML = contentHTML self.contentText = contentText self.summary = summary @@ -53,8 +55,13 @@ final class ArticleSearchInfo: Hashable { } convenience init(article: Article) { - let authorsNames = article.authors?.map({ $0.name }).reduce("", { $0.appending("").appending($1 ?? "") }).collapsingWhitespace - self.init(articleID: article.articleID, title: article.title, authorsNames: authorsNames, contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, searchRowID: nil) + let authorsNames: String? + if let authors = article.authors { + authorsNames = authors.compactMap({ $0.name }).joined(separator: " ") + } else { + authorsNames = nil + } + self.init(articleID: article.articleID, title: article.title, contentHTML: article.contentHTML, contentText: article.contentText, summary: article.summary, authorsNames: authorsNames, searchRowID: nil) } // MARK: Hashable @@ -66,7 +73,7 @@ final class ArticleSearchInfo: Hashable { // MARK: Equatable static func == (lhs: ArticleSearchInfo, rhs: ArticleSearchInfo) -> Bool { - return lhs.articleID == rhs.articleID && lhs.title == rhs.title && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.summary == rhs.summary && lhs.searchRowID == rhs.searchRowID + return lhs.articleID == rhs.articleID && lhs.title == rhs.title && lhs.contentHTML == rhs.contentHTML && lhs.contentText == rhs.contentText && lhs.summary == rhs.summary && lhs.authorsNames == rhs.authorsNames && lhs.searchRowID == rhs.searchRowID } } From 5ef7ef057ed3adfba04b8d2c841b4d0071977ad2 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 13 Feb 2021 16:11:23 -0600 Subject: [PATCH 14/17] Update to latest RSCore --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0ca6b48cc..71a837402 100644 --- a/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/NetNewsWire.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/Ranchero-Software/RSCore.git", "state": { "branch": null, - "revision": "55295e826ac0249ac0c271e83c4489313b350a7c", - "version": "1.0.1" + "revision": "6b2ef2580968905af825c40442dc0ba3126032c0", + "version": "1.0.2" } }, { From ca294c7528934c786f582415eee87a479df6588f Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 15 Feb 2021 17:49:28 -0600 Subject: [PATCH 15/17] Remove async call since the Big Sur bug that it was working around appears to be fixed. Fixes #2791 --- Mac/MainWindow/MainWindowController.swift | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 0c911b99f..bdacce4db 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -62,14 +62,12 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { sharingServicePickerDelegate = SharingServicePickerDelegate(self.window) if #available(macOS 11.0, *) { - DispatchQueue.main.async { - let toolbar = NSToolbar(identifier: "MainWindowToolbar") - toolbar.allowsUserCustomization = true - toolbar.autosavesConfiguration = true - toolbar.displayMode = .iconOnly - toolbar.delegate = self - self.window?.toolbar = toolbar - } + let toolbar = NSToolbar(identifier: "MainWindowToolbar") + toolbar.allowsUserCustomization = true + toolbar.autosavesConfiguration = true + toolbar.displayMode = .iconOnly + toolbar.delegate = self + self.window?.toolbar = toolbar } else { if !AppDefaults.shared.showTitleOnMainWindow { window?.titleVisibility = .hidden From 23bb0d1120afd2b224bc668706b0446217f99927 Mon Sep 17 00:00:00 2001 From: Stuart Breckenridge Date: Tue, 16 Feb 2021 09:56:20 +0800 Subject: [PATCH 16/17] Fixes #2781 --- .../MasterTimelineViewController.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index d92638b33..27462c202 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -471,13 +471,15 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner } @objc func userDefaultsDidChange(_ note: Notification) { - if numberOfTextLines != AppDefaults.shared.timelineNumberOfLines || iconSize != AppDefaults.shared.timelineIconSize { - numberOfTextLines = AppDefaults.shared.timelineNumberOfLines - iconSize = AppDefaults.shared.timelineIconSize - resetEstimatedRowHeight() - reloadAllVisibleCells() + DispatchQueue.main.async { + if self.numberOfTextLines != AppDefaults.shared.timelineNumberOfLines || self.iconSize != AppDefaults.shared.timelineIconSize { + self.numberOfTextLines = AppDefaults.shared.timelineNumberOfLines + self.iconSize = AppDefaults.shared.timelineIconSize + self.resetEstimatedRowHeight() + self.reloadAllVisibleCells() + } + self.updateToolbar() } - updateToolbar() } @objc func contentSizeCategoryDidChange(_ note: Notification) { From 731d0d0d88c7253b6768d623d61ec5423482bc60 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Tue, 16 Feb 2021 14:24:21 -0600 Subject: [PATCH 17/17] Show Folder as it is created to give better visual feedback that a folder copy was successful. Fixes #2815 --- Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift index 11ad9399f..f403d8b25 100644 --- a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift +++ b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift @@ -490,10 +490,8 @@ private extension SidebarOutlineDataSource { return } - BatchUpdate.shared.start() replicateFolder(sourceFolder, destinationAccount: destinationAccount) { sourceAccount.removeFolder(sourceFolder) { result in - BatchUpdate.shared.end() switch result { case .success: break