Change from using @Published as a PassthroughSubject to using real ones to avoid @Published quirks
This commit is contained in:
parent
7c9484bba1
commit
b2c70e847c
@ -115,8 +115,8 @@ extension SceneModel: SidebarModelDelegate {
|
|||||||
|
|
||||||
extension SceneModel: TimelineModelDelegate {
|
extension SceneModel: TimelineModelDelegate {
|
||||||
|
|
||||||
var selectedFeeds: Published<[Feed]>.Publisher {
|
var selectedFeedsPublisher: AnyPublisher<[Feed], Never>? {
|
||||||
return sidebarModel.$selectedFeeds
|
return sidebarModel.selectedFeedsPublisher
|
||||||
}
|
}
|
||||||
|
|
||||||
func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed) {
|
func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed) {
|
||||||
|
@ -31,7 +31,7 @@ struct SidebarContextMenu: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
sidebarModel.markAllAsRead(account: sidebarItem.represented as! Account)
|
// sidebarModel.markAllAsRead(account: sidebarItem.represented as! Account)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Mark All As Read")
|
Text("Mark All As Read")
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -46,7 +46,7 @@ struct SidebarContextMenu: View {
|
|||||||
guard let feed = sidebarItem.feed else {
|
guard let feed = sidebarItem.feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sidebarModel.markAllAsRead(feed: feed)
|
sidebarModel.markAllAsReadInFeed.send(feed)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Mark All As Read")
|
Text("Mark All As Read")
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -69,7 +69,7 @@ struct SidebarContextMenu: View {
|
|||||||
guard let feed = sidebarItem.feed else {
|
guard let feed = sidebarItem.feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sidebarModel.markAllAsRead(feed: feed)
|
sidebarModel.markAllAsReadInFeed.send(feed)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Mark All As Read")
|
Text("Mark All As Read")
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -123,7 +123,7 @@ struct SidebarContextMenu: View {
|
|||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
Button {
|
Button {
|
||||||
sidebarModel.deleteItems(item: sidebarItem)
|
// sidebarModel.deleteItems(item: sidebarItem)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Delete")
|
Text("Delete")
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -146,7 +146,7 @@ struct SidebarContextMenu: View {
|
|||||||
guard let feed = sidebarItem.feed else {
|
guard let feed = sidebarItem.feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sidebarModel.markAllAsRead(feed: feed)
|
sidebarModel.markAllAsReadInFeed.send(feed)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Mark All As Read")
|
Text("Mark All As Read")
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -155,7 +155,7 @@ struct SidebarContextMenu: View {
|
|||||||
}
|
}
|
||||||
Divider()
|
Divider()
|
||||||
Button {
|
Button {
|
||||||
sidebarModel.deleteItems(item: sidebarItem)
|
// sidebarModel.deleteItems(item: sidebarItem)
|
||||||
} label: {
|
} label: {
|
||||||
Text("Delete")
|
Text("Delete")
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
@ -18,15 +18,18 @@ protocol SidebarModelDelegate: class {
|
|||||||
|
|
||||||
class SidebarModel: ObservableObject, UndoableCommandRunner {
|
class SidebarModel: ObservableObject, UndoableCommandRunner {
|
||||||
|
|
||||||
|
@Published var selectedFeedIdentifiers = Set<FeedIdentifier>()
|
||||||
|
@Published var selectedFeedIdentifier: FeedIdentifier? = .none
|
||||||
|
@Published var isReadFiltered = false
|
||||||
|
|
||||||
weak var delegate: SidebarModelDelegate?
|
weak var delegate: SidebarModelDelegate?
|
||||||
|
|
||||||
var sidebarItemsPublisher: AnyPublisher<[SidebarItem], Never>?
|
var sidebarItemsPublisher: AnyPublisher<[SidebarItem], Never>?
|
||||||
var selectNextUnread = PassthroughSubject<Bool, Never>()
|
var selectedFeedsPublisher: AnyPublisher<[Feed], Never>?
|
||||||
|
|
||||||
@Published var selectedFeedIdentifiers = Set<FeedIdentifier>()
|
var selectNextUnread = PassthroughSubject<Bool, Never>()
|
||||||
@Published var selectedFeedIdentifier: FeedIdentifier? = .none
|
var markAllAsReadInFeed = PassthroughSubject<Feed, Never>()
|
||||||
@Published var selectedFeeds = [Feed]()
|
var markAllAsReadInAccount = PassthroughSubject<Feed, Never>()
|
||||||
@Published var isReadFiltered = false
|
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
@ -37,29 +40,37 @@ class SidebarModel: ObservableObject, UndoableCommandRunner {
|
|||||||
subscribeToSelectedFeedChanges()
|
subscribeToSelectedFeedChanges()
|
||||||
subscribeToRebuildSidebarItemsEvents()
|
subscribeToRebuildSidebarItemsEvents()
|
||||||
subscribeToNextUnread()
|
subscribeToNextUnread()
|
||||||
|
subscribeToMarkAllAsReadInFeed()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Side Context Menu Actions
|
// MARK: Side Context Menu Actions
|
||||||
|
|
||||||
extension SidebarModel {
|
private extension SidebarModel {
|
||||||
|
|
||||||
func markAllAsRead(feed: Feed) {
|
func subscribeToMarkAllAsReadInFeed() {
|
||||||
var articles = Set<Article>()
|
guard let selectedFeedsPublisher = selectedFeedsPublisher else { return }
|
||||||
let fetchedArticles = try! feed.fetchArticles()
|
|
||||||
for article in fetchedArticles {
|
|
||||||
articles.insert(article)
|
|
||||||
}
|
|
||||||
|
|
||||||
for selectedFeed in selectedFeeds {
|
markAllAsReadInFeed
|
||||||
let fetchedArticles = try! selectedFeed.fetchArticles()
|
.withLatestFrom(selectedFeedsPublisher, resultSelector: { givenFeed, selectedFeeds -> [Feed] in
|
||||||
for article in fetchedArticles {
|
if selectedFeeds.contains(where: { $0.feedID == givenFeed.feedID }) {
|
||||||
articles.insert(article)
|
return selectedFeeds
|
||||||
|
} else {
|
||||||
|
return [givenFeed]
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.map { feeds in
|
||||||
|
var articles = [Article]()
|
||||||
|
for feed in feeds {
|
||||||
|
articles.append(contentsOf: (try? feed.fetchArticles()) ?? Set<Article>())
|
||||||
}
|
}
|
||||||
|
return articles
|
||||||
markAllAsRead(Array(articles))
|
}
|
||||||
|
.sink { [weak self] allArticles in
|
||||||
|
self?.markAllAsRead(allArticles)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
func markAllAsRead(account: Account) {
|
func markAllAsRead(account: Account) {
|
||||||
@ -78,51 +89,50 @@ extension SidebarModel {
|
|||||||
private func markAllAsRead(_ articles: [Article]) {
|
private func markAllAsRead(_ articles: [Article]) {
|
||||||
guard let undoManager = undoManager ?? UndoManager(),
|
guard let undoManager = undoManager ?? UndoManager(),
|
||||||
let markAsReadCommand = MarkStatusCommand(initialArticles: articles, markingRead: true, undoManager: undoManager) else {
|
let markAsReadCommand = MarkStatusCommand(initialArticles: articles, markingRead: true, undoManager: undoManager) else {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runCommand(markAsReadCommand)
|
runCommand(markAsReadCommand)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteItems(item: SidebarItem) {
|
func deleteItems(item: SidebarItem) {
|
||||||
#if os(macOS)
|
// #if os(macOS)
|
||||||
if selectedFeeds.count > 0 {
|
// if selectedFeeds.count > 0 {
|
||||||
for feed in selectedFeeds {
|
// for feed in selectedFeeds {
|
||||||
if feed is WebFeed {
|
// if feed is WebFeed {
|
||||||
print(feed.nameForDisplay)
|
// print(feed.nameForDisplay)
|
||||||
let account = (feed as! WebFeed).account
|
// let account = (feed as! WebFeed).account
|
||||||
account?.removeWebFeed(feed as! WebFeed)
|
// account?.removeWebFeed(feed as! WebFeed)
|
||||||
}
|
// }
|
||||||
if feed is Folder {
|
// if feed is Folder {
|
||||||
let account = (feed as! Folder).account
|
// let account = (feed as! Folder).account
|
||||||
account?.removeFolder(feed as! Folder, completion: { (result) in
|
// account?.removeFolder(feed as! Folder, completion: { (result) in
|
||||||
switch result {
|
// switch result {
|
||||||
case .success( _):
|
// case .success( _):
|
||||||
print("Deleted folder")
|
// print("Deleted folder")
|
||||||
case .failure(let err):
|
// case .failure(let err):
|
||||||
print(err.localizedDescription)
|
// print(err.localizedDescription)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
#else
|
// #else
|
||||||
if item.feed is WebFeed {
|
// if item.feed is WebFeed {
|
||||||
let account = (item.feed as! WebFeed).account
|
// let account = (item.feed as! WebFeed).account
|
||||||
account?.removeWebFeed(item.feed as! WebFeed)
|
// account?.removeWebFeed(item.feed as! WebFeed)
|
||||||
}
|
// }
|
||||||
if item.feed is Folder {
|
// if item.feed is Folder {
|
||||||
let account = (item.feed as! Folder).account
|
// let account = (item.feed as! Folder).account
|
||||||
account?.removeFolder(item.feed as! Folder, completion: { (result) in
|
// account?.removeFolder(item.feed as! Folder, completion: { (result) in
|
||||||
switch result {
|
// switch result {
|
||||||
case .success( _):
|
// case .success( _):
|
||||||
print("Deleted folder")
|
// print("Deleted folder")
|
||||||
case .failure(let err):
|
// case .failure(let err):
|
||||||
print(err.localizedDescription)
|
// print(err.localizedDescription)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
#endif
|
// #endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,22 +145,31 @@ private extension SidebarModel {
|
|||||||
// MARK: Subscriptions
|
// MARK: Subscriptions
|
||||||
|
|
||||||
func subscribeToSelectedFeedChanges() {
|
func subscribeToSelectedFeedChanges() {
|
||||||
$selectedFeedIdentifiers.map { [weak self] feedIDs in
|
let selectedFeedIdentifersPublisher = $selectedFeedIdentifiers
|
||||||
feedIDs.compactMap { self?.findFeed($0) }
|
.map { [weak self] feedIDs -> [Feed] in
|
||||||
|
return feedIDs.compactMap { self?.findFeed($0) }
|
||||||
}
|
}
|
||||||
.assign(to: &$selectedFeeds)
|
|
||||||
|
|
||||||
$selectedFeedIdentifier.compactMap { [weak self] feedID in
|
let selectedFeedIdentiferPublisher = $selectedFeedIdentifier
|
||||||
|
.compactMap { [weak self] feedID -> [Feed]? in
|
||||||
if let feedID = feedID, let feed = self?.findFeed(feedID) {
|
if let feedID = feedID, let feed = self?.findFeed(feedID) {
|
||||||
return [feed]
|
return [feed]
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.assign(to: &$selectedFeeds)
|
|
||||||
|
selectedFeedsPublisher = selectedFeedIdentifersPublisher
|
||||||
|
.merge(with: selectedFeedIdentiferPublisher)
|
||||||
|
.removeDuplicates(by: { previousFeeds, currentFeeds in
|
||||||
|
return previousFeeds.elementsEqual(currentFeeds, by: { $0.feedID == $1.feedID })
|
||||||
|
})
|
||||||
|
.eraseToAnyPublisher()
|
||||||
}
|
}
|
||||||
|
|
||||||
func subscribeToRebuildSidebarItemsEvents() {
|
func subscribeToRebuildSidebarItemsEvents() {
|
||||||
|
guard let selectedFeedsPublisher = selectedFeedsPublisher else { return }
|
||||||
|
|
||||||
let chidrenDidChangePublisher = NotificationCenter.default.publisher(for: .ChildrenDidChange)
|
let chidrenDidChangePublisher = NotificationCenter.default.publisher(for: .ChildrenDidChange)
|
||||||
let batchUpdateDidPerformPublisher = NotificationCenter.default.publisher(for: .BatchUpdateDidPerform)
|
let batchUpdateDidPerformPublisher = NotificationCenter.default.publisher(for: .BatchUpdateDidPerform)
|
||||||
let displayNameDidChangePublisher = NotificationCenter.default.publisher(for: .DisplayNameDidChange)
|
let displayNameDidChangePublisher = NotificationCenter.default.publisher(for: .DisplayNameDidChange)
|
||||||
@ -173,7 +192,7 @@ private extension SidebarModel {
|
|||||||
sidebarItemsPublisher = sidebarRebuildPublishers
|
sidebarItemsPublisher = sidebarRebuildPublishers
|
||||||
.prepend(kickStarter)
|
.prepend(kickStarter)
|
||||||
.debounce(for: .milliseconds(500), scheduler: RunLoop.main)
|
.debounce(for: .milliseconds(500), scheduler: RunLoop.main)
|
||||||
.combineLatest($isReadFiltered, $selectedFeeds)
|
.combineLatest($isReadFiltered.removeDuplicates(), selectedFeedsPublisher)
|
||||||
.compactMap { [weak self] _, readFilter, selectedFeeds in
|
.compactMap { [weak self] _, readFilter, selectedFeeds in
|
||||||
self?.rebuildSidebarItems(isReadFiltered: readFilter, selectedFeeds: selectedFeeds)
|
self?.rebuildSidebarItems(isReadFiltered: readFilter, selectedFeeds: selectedFeeds)
|
||||||
}
|
}
|
||||||
@ -181,10 +200,10 @@ private extension SidebarModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func subscribeToNextUnread() {
|
func subscribeToNextUnread() {
|
||||||
guard let sidebarItemsPublisher = sidebarItemsPublisher else { return }
|
guard let sidebarItemsPublisher = sidebarItemsPublisher, let selectedFeedsPublisher = selectedFeedsPublisher else { return }
|
||||||
|
|
||||||
selectNextUnread
|
selectNextUnread
|
||||||
.withLatestFrom(sidebarItemsPublisher, $selectedFeeds)
|
.withLatestFrom(sidebarItemsPublisher, selectedFeedsPublisher)
|
||||||
.compactMap { [weak self] (sidebarItems, selectedFeeds) in
|
.compactMap { [weak self] (sidebarItems, selectedFeeds) in
|
||||||
return self?.nextUnread(sidebarItems: sidebarItems, selectedFeeds: selectedFeeds)
|
return self?.nextUnread(sidebarItems: sidebarItems, selectedFeeds: selectedFeeds)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import Account
|
|||||||
import Articles
|
import Articles
|
||||||
|
|
||||||
protocol TimelineModelDelegate: class {
|
protocol TimelineModelDelegate: class {
|
||||||
var selectedFeeds: Published<[Feed]>.Publisher { get }
|
var selectedFeedsPublisher: AnyPublisher<[Feed], Never>? { get }
|
||||||
func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed)
|
func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +136,8 @@ class TimelineModel: ObservableObject, UndoableCommandRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func subscribeToSelectedFeedChanges() {
|
func subscribeToSelectedFeedChanges() {
|
||||||
delegate?.selectedFeeds.sink { [weak self] feeds in
|
guard let selectedFeedsPublisher = delegate?.selectedFeedsPublisher else { return }
|
||||||
|
selectedFeedsPublisher.sink { [weak self] feeds in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.feeds = feeds
|
self.feeds = feeds
|
||||||
self.fetchArticles()
|
self.fetchArticles()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user