2020-06-28 21:21:43 +02:00
|
|
|
//
|
|
|
|
// SceneModel.swift
|
|
|
|
// NetNewsWire
|
|
|
|
//
|
|
|
|
// Created by Maurice Parker on 6/28/20.
|
|
|
|
// Copyright © 2020 Ranchero Software. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
2020-07-12 02:52:28 +02:00
|
|
|
import Combine
|
2020-06-29 00:43:20 +02:00
|
|
|
import Account
|
2020-07-07 04:14:05 +02:00
|
|
|
import Articles
|
2020-07-09 23:34:47 +02:00
|
|
|
import RSCore
|
2020-06-28 21:21:43 +02:00
|
|
|
|
|
|
|
final class SceneModel: ObservableObject {
|
|
|
|
|
2020-07-14 00:24:40 +02:00
|
|
|
@Published var markAllAsReadButtonState: Bool?
|
|
|
|
@Published var nextUnreadButtonState: Bool?
|
2020-07-13 23:26:53 +02:00
|
|
|
@Published var readButtonState: Bool?
|
|
|
|
@Published var starButtonState: Bool?
|
2020-07-14 00:24:40 +02:00
|
|
|
@Published var extractorButtonState: ArticleExtractorButtonState?
|
|
|
|
@Published var openInBrowserButtonState: Bool?
|
|
|
|
@Published var shareButtonState: Bool?
|
2020-07-10 01:44:51 +02:00
|
|
|
|
2020-07-16 03:06:29 +02:00
|
|
|
@Published var accountErrorMessage = ""
|
|
|
|
|
2020-07-14 03:18:39 +02:00
|
|
|
var selectedArticles: [Article] {
|
|
|
|
timelineModel.selectedArticles
|
|
|
|
}
|
|
|
|
|
2020-07-10 01:44:51 +02:00
|
|
|
private var refreshProgressModel: RefreshProgressModel? = nil
|
|
|
|
private var articleIconSchemeHandler: ArticleIconSchemeHandler? = nil
|
|
|
|
|
2020-07-12 01:22:47 +02:00
|
|
|
private(set) var webViewProvider: WebViewProvider? = nil
|
|
|
|
private(set) var sidebarModel = SidebarModel()
|
|
|
|
private(set) var timelineModel = TimelineModel()
|
2020-07-10 01:44:51 +02:00
|
|
|
|
2020-07-12 02:52:28 +02:00
|
|
|
private var selectedArticlesCancellable: AnyCancellable?
|
|
|
|
|
2020-07-09 23:34:47 +02:00
|
|
|
// MARK: Initialization API
|
2020-07-07 04:14:05 +02:00
|
|
|
|
2020-07-10 01:44:51 +02:00
|
|
|
/// Prepares the SceneModel to be used in the views
|
2020-07-06 02:49:07 +02:00
|
|
|
func startup() {
|
2020-07-12 01:22:47 +02:00
|
|
|
sidebarModel.delegate = self
|
|
|
|
timelineModel.delegate = self
|
2020-07-07 04:14:05 +02:00
|
|
|
|
2020-07-08 17:20:04 +02:00
|
|
|
self.articleIconSchemeHandler = ArticleIconSchemeHandler(sceneModel: self)
|
|
|
|
self.webViewProvider = WebViewProvider(articleIconSchemeHandler: self.articleIconSchemeHandler!)
|
2020-07-10 01:44:51 +02:00
|
|
|
|
|
|
|
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
2020-07-12 02:52:28 +02:00
|
|
|
|
|
|
|
selectedArticlesCancellable = timelineModel.$selectedArticles.sink { [weak self] articles in
|
2020-07-14 00:24:40 +02:00
|
|
|
self?.updateArticleButtonsState(articles: articles)
|
2020-07-12 02:52:28 +02:00
|
|
|
}
|
2020-07-07 04:14:05 +02:00
|
|
|
}
|
2020-07-09 23:34:47 +02:00
|
|
|
|
2020-07-10 01:44:51 +02:00
|
|
|
// MARK: Article Management API
|
2020-07-09 23:34:47 +02:00
|
|
|
|
2020-07-12 02:52:28 +02:00
|
|
|
func toggleReadStatusForSelectedArticles() {
|
|
|
|
timelineModel.toggleReadStatusForSelectedArticles()
|
|
|
|
}
|
|
|
|
|
|
|
|
func toggleStarredStatusForSelectedArticles() {
|
|
|
|
timelineModel.toggleStarredStatusForSelectedArticles()
|
|
|
|
}
|
|
|
|
|
2020-07-10 01:44:51 +02:00
|
|
|
/// Retrieves the article before the given article in the Timeline
|
|
|
|
func findPrevArticle(_ article: Article) -> Article? {
|
2020-07-12 01:22:47 +02:00
|
|
|
return timelineModel.findPrevArticle(article)
|
2020-07-10 01:44:51 +02:00
|
|
|
}
|
2020-07-09 23:34:47 +02:00
|
|
|
|
2020-07-10 01:44:51 +02:00
|
|
|
/// Retrieves the article after the given article in the Timeline
|
|
|
|
func findNextArticle(_ article: Article) -> Article? {
|
2020-07-12 01:22:47 +02:00
|
|
|
return timelineModel.findNextArticle(article)
|
2020-07-10 01:44:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the article with the given articleID
|
2020-07-07 04:14:05 +02:00
|
|
|
func articleFor(_ articleID: String) -> Article? {
|
2020-07-12 01:22:47 +02:00
|
|
|
return timelineModel.articleFor(articleID)
|
2020-07-04 17:20:50 +02:00
|
|
|
}
|
2020-07-07 04:14:05 +02:00
|
|
|
|
2020-06-28 21:21:43 +02:00
|
|
|
}
|
2020-06-29 00:43:20 +02:00
|
|
|
|
|
|
|
// MARK: SidebarModelDelegate
|
|
|
|
|
|
|
|
extension SceneModel: SidebarModelDelegate {
|
|
|
|
|
2020-06-29 13:16:48 +02:00
|
|
|
func unreadCount(for feed: Feed) -> Int {
|
|
|
|
// TODO: Get the count from the timeline if Feed is the current timeline
|
|
|
|
return feed.unreadCount
|
|
|
|
}
|
|
|
|
|
2020-06-29 00:43:20 +02:00
|
|
|
}
|
2020-06-30 18:03:33 +02:00
|
|
|
|
|
|
|
// MARK: TimelineModelDelegate
|
|
|
|
|
|
|
|
extension SceneModel: TimelineModelDelegate {
|
|
|
|
|
|
|
|
func timelineRequestedWebFeedSelection(_: TimelineModel, webFeed: WebFeed) {
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-07-02 22:30:50 +02:00
|
|
|
|
2020-07-07 04:14:05 +02:00
|
|
|
// MARK: Private
|
2020-07-02 22:30:50 +02:00
|
|
|
|
2020-07-07 04:14:05 +02:00
|
|
|
private extension SceneModel {
|
2020-07-02 22:30:50 +02:00
|
|
|
|
2020-07-10 01:44:51 +02:00
|
|
|
// MARK: Notifications
|
|
|
|
|
|
|
|
@objc func statusesDidChange(_ note: Notification) {
|
2020-07-12 01:22:47 +02:00
|
|
|
guard let articleIDs = note.userInfo?[Account.UserInfoKey.articleIDs] as? Set<String> else {
|
2020-07-10 01:44:51 +02:00
|
|
|
return
|
|
|
|
}
|
2020-07-12 01:22:47 +02:00
|
|
|
let selectedArticleIDs = timelineModel.selectedArticles.map { $0.articleID }
|
|
|
|
if !articleIDs.intersection(selectedArticleIDs).isEmpty {
|
2020-07-14 00:24:40 +02:00
|
|
|
updateArticleButtonsState(articles: timelineModel.selectedArticles)
|
2020-07-10 01:44:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-12 01:22:47 +02:00
|
|
|
// MARK: Button State Updates
|
2020-07-10 01:44:51 +02:00
|
|
|
|
2020-07-14 00:24:40 +02:00
|
|
|
func updateArticleButtonsState(articles: [Article]) {
|
2020-07-12 01:22:47 +02:00
|
|
|
guard !articles.isEmpty else {
|
2020-07-10 01:44:51 +02:00
|
|
|
readButtonState = nil
|
|
|
|
starButtonState = nil
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-07-12 01:22:47 +02:00
|
|
|
if articles.anyArticleIsUnread() {
|
2020-07-13 23:26:53 +02:00
|
|
|
readButtonState = true
|
2020-07-12 01:22:47 +02:00
|
|
|
} else if articles.anyArticleIsReadAndCanMarkUnread() {
|
2020-07-13 23:26:53 +02:00
|
|
|
readButtonState = false
|
2020-07-10 01:44:51 +02:00
|
|
|
} else {
|
|
|
|
readButtonState = nil
|
|
|
|
}
|
|
|
|
|
2020-07-12 01:22:47 +02:00
|
|
|
if articles.anyArticleIsUnstarred() {
|
2020-07-13 23:26:53 +02:00
|
|
|
starButtonState = false
|
2020-07-12 02:52:28 +02:00
|
|
|
} else {
|
2020-07-13 23:26:53 +02:00
|
|
|
starButtonState = true
|
2020-07-12 01:22:47 +02:00
|
|
|
}
|
2020-07-14 00:24:40 +02:00
|
|
|
|
|
|
|
if articles.count == 1, articles.first?.preferredLink != nil {
|
|
|
|
openInBrowserButtonState = true
|
|
|
|
shareButtonState = true
|
|
|
|
} else {
|
|
|
|
openInBrowserButtonState = nil
|
|
|
|
shareButtonState = nil
|
|
|
|
}
|
2020-07-10 01:44:51 +02:00
|
|
|
}
|
2020-07-09 23:34:47 +02:00
|
|
|
|
2020-07-02 22:30:50 +02:00
|
|
|
}
|