mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-02-03 12:27:32 +01:00
Fix build errors.
This commit is contained in:
parent
5c6e5807d9
commit
b2d3128b2d
@ -456,7 +456,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
|
||||
public func suspendDatabase() {
|
||||
#if os(iOS)
|
||||
database.cancelAndSuspend()
|
||||
Task {
|
||||
await database.suspend()
|
||||
}
|
||||
#endif
|
||||
save()
|
||||
}
|
||||
@ -465,7 +467,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
/// Call this *before* calling resume.
|
||||
public func resumeDatabaseAndDelegate() {
|
||||
#if os(iOS)
|
||||
database.resume()
|
||||
Task {
|
||||
await database.resume()
|
||||
}
|
||||
#endif
|
||||
delegate.resume()
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ public actor ArticlesDatabase {
|
||||
#endif
|
||||
}
|
||||
|
||||
func resume() {
|
||||
public func resume() {
|
||||
#if os(iOS)
|
||||
if database == nil {
|
||||
self.database = FMDatabase.openAndSetUpDatabase(path: databasePath)
|
||||
|
@ -968,28 +968,6 @@ extension AppDelegate: NSWindowRestoration {
|
||||
|
||||
private extension AppDelegate {
|
||||
|
||||
struct ArticlePathInfo {
|
||||
|
||||
let accountID: String
|
||||
let articleID: String
|
||||
|
||||
init?(userInfo: [AnyHashable: Any]) {
|
||||
|
||||
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [String: String] else {
|
||||
return nil
|
||||
}
|
||||
guard let accountID = articlePathUserInfo[ArticlePathKey.accountID] else {
|
||||
return nil
|
||||
}
|
||||
guard let articleID = articlePathUserInfo[ArticlePathKey.articleID] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.accountID = accountID
|
||||
self.articleID = articleID
|
||||
}
|
||||
}
|
||||
|
||||
func handleMarkAsRead(userInfo: [AnyHashable: Any]) {
|
||||
|
||||
guard let articlePathInfo = ArticlePathInfo(userInfo: userInfo) else {
|
||||
|
@ -632,6 +632,9 @@
|
||||
842E45DD1ED8C54B000A8B52 /* Browser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45DC1ED8C54B000A8B52 /* Browser.swift */; };
|
||||
84411E711FE5FBFA004B527F /* SmallIconProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84411E701FE5FBFA004B527F /* SmallIconProvider.swift */; };
|
||||
8444C8F21FED81840051386C /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; };
|
||||
844933D22BA953590068AC51 /* ArticlePathInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844933D12BA953590068AC51 /* ArticlePathInfo.swift */; };
|
||||
844933D32BA953590068AC51 /* ArticlePathInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844933D12BA953590068AC51 /* ArticlePathInfo.swift */; };
|
||||
844933D42BA953590068AC51 /* ArticlePathInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844933D12BA953590068AC51 /* ArticlePathInfo.swift */; };
|
||||
844B5B591FE9FE4F00C7C76A /* SidebarKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844B5B581FE9FE4F00C7C76A /* SidebarKeyboardDelegate.swift */; };
|
||||
844B5B5B1FEA00FB00C7C76A /* TimelineKeyboardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 844B5B5A1FEA00FB00C7C76A /* TimelineKeyboardDelegate.swift */; };
|
||||
844B5B651FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 844B5B641FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist */; };
|
||||
@ -1341,6 +1344,7 @@
|
||||
842E45DC1ED8C54B000A8B52 /* Browser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Browser.swift; sourceTree = "<group>"; };
|
||||
84411E701FE5FBFA004B527F /* SmallIconProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallIconProvider.swift; sourceTree = "<group>"; };
|
||||
8444C8F11FED81840051386C /* OPMLExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLExporter.swift; sourceTree = "<group>"; };
|
||||
844933D12BA953590068AC51 /* ArticlePathInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticlePathInfo.swift; sourceTree = "<group>"; };
|
||||
844B5B581FE9FE4F00C7C76A /* SidebarKeyboardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarKeyboardDelegate.swift; sourceTree = "<group>"; };
|
||||
844B5B5A1FEA00FB00C7C76A /* TimelineKeyboardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineKeyboardDelegate.swift; sourceTree = "<group>"; };
|
||||
844B5B641FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = GlobalKeyboardShortcuts.plist; sourceTree = "<group>"; };
|
||||
@ -2445,6 +2449,7 @@
|
||||
842E45CD1ED8C308000A8B52 /* AppNotifications.swift */,
|
||||
51C4CFEF24D37D1F00AF9874 /* Secrets.swift */,
|
||||
511B9805237DCAC90028BCAA /* UserInfoKey.swift */,
|
||||
844933D12BA953590068AC51 /* ArticlePathInfo.swift */,
|
||||
8454C3F2263F2D8700E3F9C7 /* IconImageCache.swift */,
|
||||
845122752B8CEA9B00480DB0 /* SidebarItem */,
|
||||
51C452AD2265102800C03939 /* Timeline */,
|
||||
@ -3802,6 +3807,7 @@
|
||||
65ED3FFB235DEF6C0081F399 /* AccountsReaderAPIWindowController.swift in Sources */,
|
||||
65ED3FFC235DEF6C0081F399 /* AccountsAddLocalWindowController.swift in Sources */,
|
||||
65ED3FFD235DEF6C0081F399 /* PasteboardFolder.swift in Sources */,
|
||||
844933D32BA953590068AC51 /* ArticlePathInfo.swift in Sources */,
|
||||
51386A8F25673277005F3762 /* AccountCell.swift in Sources */,
|
||||
65ED3FFE235DEF6C0081F399 /* AccountsFeedbinWindowController.swift in Sources */,
|
||||
65ED3FFF235DEF6C0081F399 /* SidebarOutlineDataSource.swift in Sources */,
|
||||
@ -4039,6 +4045,7 @@
|
||||
B24E9ADE245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */,
|
||||
C5A6ED5223C9AF4300AB6BE2 /* TitleActivityItemSource.swift in Sources */,
|
||||
17D7586F2679C21800B17787 /* OnePasswordExtension.m in Sources */,
|
||||
844933D42BA953590068AC51 /* ArticlePathInfo.swift in Sources */,
|
||||
17071EF126F8137400F5E71D /* ArticleTheme+Notifications.swift in Sources */,
|
||||
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */,
|
||||
84DEE56622C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */,
|
||||
@ -4139,6 +4146,7 @@
|
||||
51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */,
|
||||
849A978A1ED9ECEF007D329B /* ArticleThemesManager.swift in Sources */,
|
||||
8405DD8A2213E0E3008CE1BF /* DetailContainerView.swift in Sources */,
|
||||
844933D22BA953590068AC51 /* ArticlePathInfo.swift in Sources */,
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */,
|
||||
FF3ABF1523259DDB0074C542 /* ArticleSorter.swift in Sources */,
|
||||
84E8E0DB202EC49300562D8F /* TimelineViewController+ContextualMenus.swift in Sources */,
|
||||
|
@ -7,7 +7,6 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreSpotlight
|
||||
import CoreServices
|
||||
import RSCore
|
||||
import Account
|
||||
@ -15,6 +14,12 @@ import Articles
|
||||
import Intents
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
#if os(iOS)
|
||||
@preconcurrency import CoreSpotlight
|
||||
#else
|
||||
import CoreSpotlight
|
||||
#endif
|
||||
|
||||
class ActivityManager {
|
||||
|
||||
private var nextUnreadActivity: NSUserActivity?
|
||||
@ -109,34 +114,45 @@ class ActivityManager {
|
||||
|
||||
#if os(iOS)
|
||||
static func cleanUp(_ account: Account) {
|
||||
var ids = [String]()
|
||||
|
||||
if let folders = account.folders {
|
||||
for folder in folders {
|
||||
ids.append(identifier(for: folder))
|
||||
|
||||
Task { @MainActor in
|
||||
var ids = [String]()
|
||||
|
||||
if let folders = account.folders {
|
||||
for folder in folders {
|
||||
ids.append(identifier(for: folder))
|
||||
}
|
||||
}
|
||||
|
||||
for feed in account.flattenedFeeds() {
|
||||
let feedIdentifiers = await identifiers(for: feed)
|
||||
ids.append(contentsOf: feedIdentifiers)
|
||||
}
|
||||
|
||||
try? await CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: ids)
|
||||
}
|
||||
|
||||
for feed in account.flattenedFeeds() {
|
||||
ids.append(contentsOf: identifiers(for: feed))
|
||||
}
|
||||
|
||||
CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: ids)
|
||||
}
|
||||
|
||||
static func cleanUp(_ folder: Folder) {
|
||||
var ids = [String]()
|
||||
ids.append(identifier(for: folder))
|
||||
|
||||
for feed in folder.flattenedFeeds() {
|
||||
ids.append(contentsOf: identifiers(for: feed))
|
||||
|
||||
Task { @MainActor in
|
||||
var ids = [String]()
|
||||
ids.append(identifier(for: folder))
|
||||
|
||||
for feed in folder.flattenedFeeds() {
|
||||
let feedIdentifiers = await identifiers(for: feed)
|
||||
ids.append(contentsOf: feedIdentifiers)
|
||||
}
|
||||
|
||||
try? await CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: ids)
|
||||
}
|
||||
|
||||
CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: ids)
|
||||
}
|
||||
|
||||
static func cleanUp(_ feed: Feed) {
|
||||
CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: identifiers(for: feed))
|
||||
Task { @MainActor in
|
||||
let feedIdentifiers = await identifiers(for: feed)
|
||||
try? await CSSearchableIndex.default().deleteSearchableItems(withIdentifiers: feedIdentifiers)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
31
Shared/ArticlePathInfo.swift
Normal file
31
Shared/ArticlePathInfo.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// ArticlePathInfo.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 3/18/24.
|
||||
// Copyright © 2024 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct ArticlePathInfo {
|
||||
|
||||
let accountID: String
|
||||
let articleID: String
|
||||
|
||||
init?(userInfo: [AnyHashable: Any]) {
|
||||
|
||||
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [String: String] else {
|
||||
return nil
|
||||
}
|
||||
guard let accountID = articlePathUserInfo[ArticlePathKey.accountID] else {
|
||||
return nil
|
||||
}
|
||||
guard let articleID = articlePathUserInfo[ArticlePathKey.articleID] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.accountID = accountID
|
||||
self.articleID = articleID
|
||||
}
|
||||
}
|
@ -31,81 +31,81 @@ public final class WidgetDataEncoder {
|
||||
func encodeWidgetData() throws {
|
||||
os_log(.debug, log: log, "Starting encoding widget data.")
|
||||
|
||||
do {
|
||||
let unreadArticles = Array(try AccountManager.shared.fetchArticles(.unread(fetchLimit))).sortedByDate(.orderedDescending)
|
||||
let starredArticles = Array(try AccountManager.shared.fetchArticles(.starred(fetchLimit))).sortedByDate(.orderedDescending)
|
||||
let todayArticles = Array(try AccountManager.shared.fetchArticles(.today(fetchLimit))).sortedByDate(.orderedDescending)
|
||||
|
||||
var unread = [LatestArticle]()
|
||||
var today = [LatestArticle]()
|
||||
var starred = [LatestArticle]()
|
||||
|
||||
for article in unreadArticles {
|
||||
let latestArticle = LatestArticle(id: article.sortableArticleID,
|
||||
feedTitle: article.sortableName,
|
||||
articleTitle: ArticleStringFormatter.truncatedTitle(article).isEmpty ? ArticleStringFormatter.truncatedSummary(article) : ArticleStringFormatter.truncatedTitle(article),
|
||||
articleSummary: article.summary,
|
||||
feedIcon: article.iconImage()?.image.dataRepresentation(),
|
||||
pubDate: article.datePublished?.description ?? "")
|
||||
unread.append(latestArticle)
|
||||
}
|
||||
|
||||
for article in starredArticles {
|
||||
let latestArticle = LatestArticle(id: article.sortableArticleID,
|
||||
feedTitle: article.sortableName,
|
||||
articleTitle: ArticleStringFormatter.truncatedTitle(article).isEmpty ? ArticleStringFormatter.truncatedSummary(article) : ArticleStringFormatter.truncatedTitle(article),
|
||||
articleSummary: article.summary,
|
||||
feedIcon: article.iconImage()?.image.dataRepresentation(),
|
||||
pubDate: article.datePublished?.description ?? "")
|
||||
starred.append(latestArticle)
|
||||
}
|
||||
|
||||
for article in todayArticles {
|
||||
let latestArticle = LatestArticle(id: article.sortableArticleID,
|
||||
feedTitle: article.sortableName,
|
||||
articleTitle: ArticleStringFormatter.truncatedTitle(article).isEmpty ? ArticleStringFormatter.truncatedSummary(article) : ArticleStringFormatter.truncatedTitle(article),
|
||||
articleSummary: article.summary,
|
||||
feedIcon: article.iconImage()?.image.dataRepresentation(),
|
||||
pubDate: article.datePublished?.description ?? "")
|
||||
today.append(latestArticle)
|
||||
}
|
||||
|
||||
let latestData = WidgetData(currentUnreadCount: SmartFeedsController.shared.unreadFeed.unreadCount,
|
||||
currentTodayCount: SmartFeedsController.shared.todayFeed.unreadCount,
|
||||
currentStarredCount: try! SmartFeedsController.shared.starredFeed.fetchArticles().count,
|
||||
unreadArticles: unread,
|
||||
starredArticles: starred,
|
||||
todayArticles:today,
|
||||
lastUpdateTime: Date())
|
||||
|
||||
|
||||
DispatchQueue.global().async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
self.backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "com.ranchero.NetNewsWire.Encode") {
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
self.backgroundTaskID = .invalid
|
||||
}
|
||||
let encodedData = try? JSONEncoder().encode(latestData)
|
||||
|
||||
os_log(.debug, log: self.log, "Finished encoding widget data.")
|
||||
|
||||
if self.fileExists() {
|
||||
try? FileManager.default.removeItem(at: self.dataURL!)
|
||||
os_log(.debug, log: self.log, "Removed widget data from container.")
|
||||
}
|
||||
if FileManager.default.createFile(atPath: self.dataURL!.path, contents: encodedData, attributes: nil) {
|
||||
os_log(.debug, log: self.log, "Wrote widget data to container.")
|
||||
WidgetCenter.shared.reloadAllTimelines()
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
self.backgroundTaskID = .invalid
|
||||
} else {
|
||||
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
self.backgroundTaskID = .invalid
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// do {
|
||||
// let unreadArticles = Array(try AccountManager.shared.fetchArticles(fetchType: .unread(fetchLimit))).sortedByDate(.orderedDescending)
|
||||
// let starredArticles = Array(try AccountManager.shared.fetchArticles(fetchType: .starred(fetchLimit))).sortedByDate(.orderedDescending)
|
||||
// let todayArticles = Array(try AccountManager.shared.fetchArticles(fetchType: .today(fetchLimit))).sortedByDate(.orderedDescending)
|
||||
//
|
||||
// var unread = [LatestArticle]()
|
||||
// var today = [LatestArticle]()
|
||||
// var starred = [LatestArticle]()
|
||||
//
|
||||
// for article in unreadArticles {
|
||||
// let latestArticle = LatestArticle(id: article.sortableArticleID,
|
||||
// feedTitle: article.sortableName,
|
||||
// articleTitle: ArticleStringFormatter.truncatedTitle(article).isEmpty ? ArticleStringFormatter.truncatedSummary(article) : ArticleStringFormatter.truncatedTitle(article),
|
||||
// articleSummary: article.summary,
|
||||
// feedIcon: article.iconImage()?.image.dataRepresentation(),
|
||||
// pubDate: article.datePublished?.description ?? "")
|
||||
// unread.append(latestArticle)
|
||||
// }
|
||||
//
|
||||
// for article in starredArticles {
|
||||
// let latestArticle = LatestArticle(id: article.sortableArticleID,
|
||||
// feedTitle: article.sortableName,
|
||||
// articleTitle: ArticleStringFormatter.truncatedTitle(article).isEmpty ? ArticleStringFormatter.truncatedSummary(article) : ArticleStringFormatter.truncatedTitle(article),
|
||||
// articleSummary: article.summary,
|
||||
// feedIcon: article.iconImage()?.image.dataRepresentation(),
|
||||
// pubDate: article.datePublished?.description ?? "")
|
||||
// starred.append(latestArticle)
|
||||
// }
|
||||
//
|
||||
// for article in todayArticles {
|
||||
// let latestArticle = LatestArticle(id: article.sortableArticleID,
|
||||
// feedTitle: article.sortableName,
|
||||
// articleTitle: ArticleStringFormatter.truncatedTitle(article).isEmpty ? ArticleStringFormatter.truncatedSummary(article) : ArticleStringFormatter.truncatedTitle(article),
|
||||
// articleSummary: article.summary,
|
||||
// feedIcon: article.iconImage()?.image.dataRepresentation(),
|
||||
// pubDate: article.datePublished?.description ?? "")
|
||||
// today.append(latestArticle)
|
||||
// }
|
||||
//
|
||||
// let latestData = WidgetData(currentUnreadCount: SmartFeedsController.shared.unreadFeed.unreadCount,
|
||||
// currentTodayCount: SmartFeedsController.shared.todayFeed.unreadCount,
|
||||
// currentStarredCount: try! SmartFeedsController.shared.starredFeed.fetchArticles().count,
|
||||
// unreadArticles: unread,
|
||||
// starredArticles: starred,
|
||||
// todayArticles:today,
|
||||
// lastUpdateTime: Date())
|
||||
//
|
||||
//
|
||||
// DispatchQueue.global().async { [weak self] in
|
||||
// guard let self = self else { return }
|
||||
//
|
||||
// self.backgroundTaskID = UIApplication.shared.beginBackgroundTask (withName: "com.ranchero.NetNewsWire.Encode") {
|
||||
// UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
// self.backgroundTaskID = .invalid
|
||||
// }
|
||||
// let encodedData = try? JSONEncoder().encode(latestData)
|
||||
//
|
||||
// os_log(.debug, log: self.log, "Finished encoding widget data.")
|
||||
//
|
||||
// if self.fileExists() {
|
||||
// try? FileManager.default.removeItem(at: self.dataURL!)
|
||||
// os_log(.debug, log: self.log, "Removed widget data from container.")
|
||||
// }
|
||||
// if FileManager.default.createFile(atPath: self.dataURL!.path, contents: encodedData, attributes: nil) {
|
||||
// os_log(.debug, log: self.log, "Wrote widget data to container.")
|
||||
// WidgetCenter.shared.reloadAllTimelines()
|
||||
// UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
// self.backgroundTaskID = .invalid
|
||||
// } else {
|
||||
// UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
|
||||
// self.backgroundTaskID = .invalid
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private func fileExists() -> Bool {
|
||||
|
@ -421,58 +421,70 @@ private extension AppDelegate {
|
||||
|
||||
private extension AppDelegate {
|
||||
|
||||
func handleMarkAsRead(userInfo: [AnyHashable: Any]) {
|
||||
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any],
|
||||
let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String,
|
||||
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else {
|
||||
return
|
||||
@MainActor func handleMarkAsRead(userInfo: [AnyHashable: Any]) {
|
||||
|
||||
guard let articlePathInfo = ArticlePathInfo(userInfo: userInfo) else {
|
||||
return
|
||||
}
|
||||
|
||||
resumeDatabaseProcessingIfNecessary()
|
||||
let account = AccountManager.shared.existingAccount(with: accountID)
|
||||
guard account != nil else {
|
||||
|
||||
guard let account = AccountManager.shared.existingAccount(with: articlePathInfo.accountID) else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
return
|
||||
}
|
||||
let article = try? account!.fetchArticles(.articleIDs([articleID]))
|
||||
guard article != nil else {
|
||||
os_log(.debug, "No article found from search using %@", articleID)
|
||||
return
|
||||
}
|
||||
account!.markArticles(article!, statusKey: .read, flag: true) { _ in }
|
||||
self.prepareAccountsForBackground()
|
||||
account!.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
let articleID = articlePathInfo.articleID
|
||||
|
||||
Task { @MainActor in
|
||||
guard let articles = try? await account.articles(for: .articleIDs([articleID])) else {
|
||||
os_log(.debug, "No article found from search using %@", articleID)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
account.markArticles(articles, statusKey: .read, flag: true) { _ in }
|
||||
|
||||
self.prepareAccountsForBackground()
|
||||
|
||||
account.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func handleMarkAsStarred(userInfo: [AnyHashable: Any]) {
|
||||
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any],
|
||||
let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String,
|
||||
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else {
|
||||
return
|
||||
@MainActor func handleMarkAsStarred(userInfo: [AnyHashable: Any]) {
|
||||
|
||||
guard let articlePathInfo = ArticlePathInfo(userInfo: userInfo) else {
|
||||
return
|
||||
}
|
||||
|
||||
resumeDatabaseProcessingIfNecessary()
|
||||
let account = AccountManager.shared.existingAccount(with: accountID)
|
||||
guard account != nil else {
|
||||
|
||||
guard let account = AccountManager.shared.existingAccount(with: articlePathInfo.accountID) else {
|
||||
os_log(.debug, "No account found from notification.")
|
||||
return
|
||||
}
|
||||
let article = try? account!.fetchArticles(.articleIDs([articleID]))
|
||||
guard article != nil else {
|
||||
os_log(.debug, "No article found from search using %@", articleID)
|
||||
return
|
||||
}
|
||||
account!.markArticles(article!, statusKey: .starred, flag: true) { _ in }
|
||||
account!.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
let articleID = articlePathInfo.articleID
|
||||
|
||||
Task { @MainActor in
|
||||
|
||||
guard let articles = try? await account.articles(for: .articleIDs([articleID])) else {
|
||||
os_log(.debug, "No article found from search using %@", articleID)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
account.markArticles(articles, statusKey: .starred, flag: true) { _ in }
|
||||
|
||||
account.syncArticleStatus(completion: { [weak self] _ in
|
||||
if !AccountManager.shared.isSuspended {
|
||||
try? WidgetDataEncoder.shared.encodeWidgetData()
|
||||
self?.prepareAccountsForBackground()
|
||||
self?.suspendApplication()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -986,6 +986,7 @@ private extension SidebarViewController {
|
||||
}
|
||||
|
||||
func copyHomePageAlertAction(indexPath: IndexPath, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
|
||||
|
||||
guard let feed = coordinator.nodeFor(indexPath)?.representedObject as? Feed,
|
||||
let homePageURL = feed.homePageURL,
|
||||
let url = URL(string: homePageURL) else {
|
||||
@ -1001,9 +1002,12 @@ private extension SidebarViewController {
|
||||
}
|
||||
|
||||
func markAllAsReadAlertAction(indexPath: IndexPath, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
|
||||
|
||||
guard let feed = coordinator.nodeFor(indexPath)?.representedObject as? Feed,
|
||||
feed.unreadCount > 0,
|
||||
let articles = try? feed.fetchArticles(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
|
||||
feed.unreadCount > 0 else {
|
||||
return nil
|
||||
}
|
||||
guard let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1016,8 +1020,13 @@ private extension SidebarViewController {
|
||||
|
||||
let action = UIAlertAction(title: title, style: .default) { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView, cancelCompletion: cancel) { [weak self] in
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
completion(true)
|
||||
|
||||
Task { @MainActor in
|
||||
if let articles = try? await feed.fetchUnreadArticles() {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
return action
|
||||
@ -1092,8 +1101,11 @@ private extension SidebarViewController {
|
||||
let title = NSString.localizedStringWithFormat(localizedMenuText as NSString, feed.nameForDisplay) as String
|
||||
let action = UIAction(title: title, image: AppAssets.markAllAsReadImage) { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
|
||||
if let articles = try? feed.fetchUnreadArticles() {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
|
||||
Task { @MainActor in
|
||||
if let articles = try? await feed.fetchUnreadArticles() {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1112,8 +1124,11 @@ private extension SidebarViewController {
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
|
||||
// If you don't have this delay the screen flashes when it executes this code
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
if let articles = try? account.fetchArticles(.unread()) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
|
||||
Task { @MainActor in
|
||||
if let articles = try? await account.articles(for: .unread()) {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,8 +51,8 @@ struct SidebarItemNode: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
@MainActor final class SceneCoordinator: NSObject, UndoableCommandRunner {
|
||||
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
var undoManager: UndoManager? {
|
||||
return rootSplitViewController.undoManager
|
||||
|
@ -867,36 +867,37 @@ private extension TimelineViewController {
|
||||
}
|
||||
|
||||
func markAllInFeedAsReadAction(_ article: Article, indexPath: IndexPath) -> UIAction? {
|
||||
guard let feed = article.feed else { return nil }
|
||||
guard let fetchedArticles = try? feed.fetchArticles() else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let articles = Array(fetchedArticles)
|
||||
guard articles.canMarkAllAsRead(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
|
||||
guard let feed = article.feed, feed.unreadCount > 0 else {
|
||||
return nil
|
||||
}
|
||||
guard let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
let localizedMenuText = NSLocalizedString("Mark All as Read in “%@”", comment: "Command")
|
||||
let title = NSString.localizedStringWithFormat(localizedMenuText as NSString, feed.nameForDisplay) as String
|
||||
|
||||
let action = UIAction(title: title, image: AppAssets.markAllAsReadImage) { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView) { [weak self] in
|
||||
self?.coordinator.markAllAsRead(articles)
|
||||
|
||||
Task { @MainActor in
|
||||
if let articles = try? await feed.fetchArticles() {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return action
|
||||
}
|
||||
|
||||
func markAllInFeedAsReadAlertAction(_ article: Article, indexPath: IndexPath, completion: @escaping (Bool) -> Void) -> UIAlertAction? {
|
||||
guard let feed = article.feed else { return nil }
|
||||
guard let fetchedArticles = try? feed.fetchArticles() else {
|
||||
|
||||
guard let feed = article.feed, feed.unreadCount > 0 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let articles = Array(fetchedArticles)
|
||||
guard articles.canMarkAllAsRead(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
|
||||
guard let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -908,8 +909,15 @@ private extension TimelineViewController {
|
||||
|
||||
let action = UIAlertAction(title: title, style: .default) { [weak self] action in
|
||||
MarkAsReadAlertController.confirm(self, coordinator: self?.coordinator, confirmTitle: title, sourceType: contentView, cancelCompletion: cancel) { [weak self] in
|
||||
self?.coordinator.markAllAsRead(articles)
|
||||
completion(true)
|
||||
|
||||
Task { @MainActor in
|
||||
if let articles = try? await feed.fetchArticles() {
|
||||
self?.coordinator.markAllAsRead(Array(articles))
|
||||
completion(true)
|
||||
} else {
|
||||
completion(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return action
|
||||
|
Loading…
x
Reference in New Issue
Block a user