mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-02-02 03:56:55 +01:00
Implement Read and Star button functionality
This commit is contained in:
parent
3e61c7044b
commit
2d57945e98
@ -12,11 +12,10 @@ import Articles
|
||||
struct ArticleContainerView: View {
|
||||
|
||||
@EnvironmentObject private var sceneModel: SceneModel
|
||||
@StateObject private var articleModel = ArticleModel()
|
||||
var article: Article
|
||||
|
||||
@ViewBuilder var body: some View {
|
||||
ArticleView(sceneModel: sceneModel, articleModel: articleModel, article: article)
|
||||
ArticleView(sceneModel: sceneModel, article: article)
|
||||
.modifier(ArticleToolbarModifier())
|
||||
}
|
||||
|
||||
|
14
Multiplatform/Shared/Article/ArticleManager.swift
Normal file
14
Multiplatform/Shared/Article/ArticleManager.swift
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// ArticleManager.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 7/9/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Articles
|
||||
|
||||
protocol ArticleManager: class {
|
||||
var currentArticle: Article? { get }
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
//
|
||||
// ArticleModel.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 7/2/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import Account
|
||||
import Articles
|
||||
|
||||
protocol ArticleModelDelegate: class {
|
||||
var articleModelWebViewProvider: WebViewProvider? { get }
|
||||
func findPrevArticle(_: ArticleModel, article: Article) -> Article?
|
||||
func findNextArticle(_: ArticleModel, article: Article) -> Article?
|
||||
func selectArticle(_: ArticleModel, article: Article)
|
||||
}
|
||||
|
||||
protocol ArticleManager: class {
|
||||
var currentArticle: Article? { get }
|
||||
}
|
||||
|
||||
class ArticleModel: ObservableObject {
|
||||
|
||||
weak var articleManager: ArticleManager?
|
||||
weak var delegate: ArticleModelDelegate?
|
||||
|
||||
var webViewProvider: WebViewProvider? {
|
||||
return delegate?.articleModelWebViewProvider
|
||||
}
|
||||
|
||||
var currentArticle: Article? {
|
||||
return articleManager?.currentArticle
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
|
||||
func findPrevArticle(_ article: Article) -> Article? {
|
||||
return delegate?.findPrevArticle(self, article: article)
|
||||
}
|
||||
|
||||
func findNextArticle(_ article: Article) -> Article? {
|
||||
return delegate?.findNextArticle(self, article: article)
|
||||
}
|
||||
|
||||
func selectArticle(_ article: Article) {
|
||||
delegate?.selectArticle(self, article: article)
|
||||
}
|
||||
|
||||
func toggleReadForCurrentArticle() {
|
||||
if let article = currentArticle {
|
||||
markArticles([article], statusKey: .starred, flag: !article.status.starred)
|
||||
}
|
||||
}
|
||||
|
||||
func toggleStarForCurrentArticle() {
|
||||
if let article = currentArticle {
|
||||
markArticles([article], statusKey: .starred, flag: !article.status.starred)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ import SwiftUI
|
||||
|
||||
struct ArticleToolbarModifier: ViewModifier {
|
||||
|
||||
@EnvironmentObject private var sceneModel: SceneModel
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.toolbar {
|
||||
@ -31,11 +33,15 @@ struct ArticleToolbarModifier: ViewModifier {
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .bottomBar) {
|
||||
Button(action: {
|
||||
}, label: {
|
||||
AppAssets.readOpenImage
|
||||
.font(.title3)
|
||||
}).help("Mark as Unread")
|
||||
Button(action: { sceneModel.toggleReadForCurrentArticle() }, label: {
|
||||
if sceneModel.readButtonState == .on {
|
||||
AppAssets.readClosedImage
|
||||
} else {
|
||||
AppAssets.readOpenImage
|
||||
}
|
||||
})
|
||||
.disabled(sceneModel.readButtonState == nil ? true : false)
|
||||
.help(sceneModel.readButtonState == .on ? "Mark as Unread" : "Mark as Read")
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .bottomBar) {
|
||||
@ -43,11 +49,15 @@ struct ArticleToolbarModifier: ViewModifier {
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .bottomBar) {
|
||||
Button(action: {
|
||||
}, label: {
|
||||
AppAssets.starOpenImage
|
||||
.font(.title3)
|
||||
}).help("Mark as Starred")
|
||||
Button(action: { sceneModel.toggleStarForCurrentArticle() }, label: {
|
||||
if sceneModel.starButtonState == .on {
|
||||
AppAssets.starClosedImage
|
||||
} else {
|
||||
AppAssets.starOpenImage
|
||||
}
|
||||
})
|
||||
.disabled(sceneModel.starButtonState == nil ? true : false)
|
||||
.help(sceneModel.starButtonState == .on ? "Mark as Unstarred" : "Mark as Starred")
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .bottomBar) {
|
||||
|
@ -14,49 +14,90 @@ import RSCore
|
||||
final class SceneModel: ObservableObject {
|
||||
|
||||
@Published var refreshProgressState = RefreshProgressModel.State.none
|
||||
|
||||
@Published var readButtonState: ArticleReadButtonState?
|
||||
@Published var starButtonState: ArticleStarButtonState?
|
||||
|
||||
private var refreshProgressModel: RefreshProgressModel? = nil
|
||||
private var articleIconSchemeHandler: ArticleIconSchemeHandler? = nil
|
||||
|
||||
var webViewProvider: WebViewProvider? = nil
|
||||
|
||||
var undoManager: UndoManager?
|
||||
var undoableCommands = [UndoableCommand]()
|
||||
|
||||
var sidebarModel: SidebarModel?
|
||||
var timelineModel: TimelineModel?
|
||||
var articleModel: ArticleModel?
|
||||
|
||||
private var refreshProgressModel: RefreshProgressModel? = nil
|
||||
private var articleIconSchemeHandler: ArticleIconSchemeHandler? = nil
|
||||
private var webViewProvider: WebViewProvider? = nil
|
||||
var articleManager: ArticleManager?
|
||||
|
||||
var currentArticle: Article? {
|
||||
return articleManager?.currentArticle
|
||||
}
|
||||
|
||||
// MARK: Initialization API
|
||||
|
||||
/// Prepares the SceneModel to be used in the views
|
||||
func startup() {
|
||||
self.refreshProgressModel = RefreshProgressModel()
|
||||
self.refreshProgressModel!.$state.assign(to: self.$refreshProgressState)
|
||||
|
||||
self.articleIconSchemeHandler = ArticleIconSchemeHandler(sceneModel: self)
|
||||
self.webViewProvider = WebViewProvider(articleIconSchemeHandler: self.articleIconSchemeHandler!)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||
}
|
||||
|
||||
// MARK: Article Status Change API
|
||||
// MARK: Article Management API
|
||||
|
||||
/// Toggles the read indicator for the currently viewable article
|
||||
func toggleReadForCurrentArticle() {
|
||||
articleModel?.toggleReadForCurrentArticle()
|
||||
if let article = articleManager?.currentArticle {
|
||||
toggleRead(article)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Toggles the read indicator for the given article
|
||||
func toggleRead(_ article: Article) {
|
||||
guard !article.status.read || article.isAvailableToMarkUnread else { return }
|
||||
markArticles([article], statusKey: .read, flag: !article.status.read)
|
||||
}
|
||||
|
||||
/// Toggles the star indicator for the currently viewable article
|
||||
func toggleStarForCurrentArticle() {
|
||||
articleModel?.toggleStarForCurrentArticle()
|
||||
if let article = articleManager?.currentArticle {
|
||||
toggleStar(article)
|
||||
}
|
||||
}
|
||||
|
||||
/// Toggles the star indicator for the given article
|
||||
func toggleStar(_ article: Article) {
|
||||
markArticles([article], statusKey: .starred, flag: !article.status.starred)
|
||||
}
|
||||
|
||||
// MARK: Resource lookup API
|
||||
/// Retrieves the article before the given article in the Timeline
|
||||
func findPrevArticle(_ article: Article) -> Article? {
|
||||
return timelineModel?.findPrevArticle(article)
|
||||
}
|
||||
|
||||
/// Retrieves the article after the given article in the Timeline
|
||||
func findNextArticle(_ article: Article) -> Article? {
|
||||
return timelineModel?.findNextArticle(article)
|
||||
}
|
||||
|
||||
/// Marks the article as read and selects it in the Timeline. Don't call until after the ArticleManager article has been set.
|
||||
func updateArticleSelection() {
|
||||
guard let article = currentArticle else { return }
|
||||
|
||||
timelineModel?.selectArticle(article)
|
||||
|
||||
if article.status.read {
|
||||
updateArticleState()
|
||||
} else {
|
||||
markArticles([article], statusKey: .read, flag: true)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the article with the given articleID
|
||||
func articleFor(_ articleID: String) -> Article? {
|
||||
return timelineModel?.articleFor(articleID)
|
||||
}
|
||||
@ -83,28 +124,6 @@ extension SceneModel: TimelineModelDelegate {
|
||||
|
||||
}
|
||||
|
||||
// MARK: ArticleModelDelegate
|
||||
|
||||
extension SceneModel: ArticleModelDelegate {
|
||||
|
||||
var articleModelWebViewProvider: WebViewProvider? {
|
||||
return webViewProvider
|
||||
}
|
||||
|
||||
func findPrevArticle(_: ArticleModel, article: Article) -> Article? {
|
||||
return timelineModel?.findPrevArticle(article)
|
||||
}
|
||||
|
||||
func findNextArticle(_: ArticleModel, article: Article) -> Article? {
|
||||
return timelineModel?.findNextArticle(article)
|
||||
}
|
||||
|
||||
func selectArticle(_: ArticleModel, article: Article) {
|
||||
timelineModel?.selectArticle(article)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: UndoableCommandRunner
|
||||
|
||||
extension SceneModel: UndoableCommandRunner {
|
||||
@ -122,5 +141,33 @@ extension SceneModel: UndoableCommandRunner {
|
||||
|
||||
private extension SceneModel {
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc func statusesDidChange(_ note: Notification) {
|
||||
guard let article = currentArticle, let articleIDs = note.userInfo?[Account.UserInfoKey.articleIDs] as? Set<String> else {
|
||||
return
|
||||
}
|
||||
if articleIDs.contains(article.articleID) {
|
||||
updateArticleState()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: State Updates
|
||||
|
||||
func updateArticleState() {
|
||||
guard let article = currentArticle else {
|
||||
readButtonState = nil
|
||||
starButtonState = nil
|
||||
return
|
||||
}
|
||||
|
||||
if article.isAvailableToMarkUnread {
|
||||
readButtonState = article.status.read ? .off : .on
|
||||
} else {
|
||||
readButtonState = nil
|
||||
}
|
||||
|
||||
starButtonState = article.status.starred ? .on : .off
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -100,14 +100,26 @@ struct SceneNavigationView: View {
|
||||
}).help("Go to Next Unread").padding(.trailing, 40)
|
||||
}
|
||||
ToolbarItem {
|
||||
Button(action: {}, label: {
|
||||
AppAssets.starOpenImage
|
||||
}).help("Mark as Starred")
|
||||
Button(action: { sceneModel.toggleReadForCurrentArticle() }, label: {
|
||||
if sceneModel.readButtonState == .on {
|
||||
AppAssets.readClosedImage
|
||||
} else {
|
||||
AppAssets.readOpenImage
|
||||
}
|
||||
})
|
||||
.disabled(sceneModel.readButtonState == nil ? true : false)
|
||||
.help(sceneModel.readButtonState == .on ? "Mark as Unread" : "Mark as Read")
|
||||
}
|
||||
ToolbarItem {
|
||||
Button(action: {}, label: {
|
||||
AppAssets.readClosedImage
|
||||
}).help("Mark as Unread")
|
||||
Button(action: { sceneModel.toggleStarForCurrentArticle() }, label: {
|
||||
if sceneModel.starButtonState == .on {
|
||||
AppAssets.starClosedImage
|
||||
} else {
|
||||
AppAssets.starOpenImage
|
||||
}
|
||||
})
|
||||
.disabled(sceneModel.starButtonState == nil ? true : false)
|
||||
.help(sceneModel.starButtonState == .on ? "Mark as Unstarred" : "Mark as Starred")
|
||||
}
|
||||
ToolbarItem {
|
||||
Button(action: {}, label: {
|
||||
|
@ -12,21 +12,18 @@ import Articles
|
||||
final class ArticleView: UIViewControllerRepresentable {
|
||||
|
||||
var sceneModel: SceneModel
|
||||
var articleModel: ArticleModel
|
||||
var article: Article
|
||||
|
||||
init(sceneModel: SceneModel, articleModel: ArticleModel, article: Article) {
|
||||
init(sceneModel: SceneModel, article: Article) {
|
||||
self.sceneModel = sceneModel
|
||||
self.articleModel = articleModel
|
||||
self.article = article
|
||||
sceneModel.articleModel = articleModel
|
||||
articleModel.delegate = sceneModel
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> ArticleViewController {
|
||||
let controller = ArticleViewController()
|
||||
controller.articleModel = articleModel
|
||||
controller.article = article
|
||||
sceneModel.articleManager = controller
|
||||
controller.sceneModel = sceneModel
|
||||
controller.currentArticle = article
|
||||
return controller
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ import SafariServices
|
||||
|
||||
class ArticleViewController: UIViewController, ArticleManager {
|
||||
|
||||
weak var articleModel: ArticleModel?
|
||||
weak var sceneModel: SceneModel?
|
||||
|
||||
private var pageViewController: UIPageViewController!
|
||||
|
||||
@ -24,8 +24,8 @@ class ArticleViewController: UIViewController, ArticleManager {
|
||||
|
||||
var currentArticle: Article? {
|
||||
didSet {
|
||||
if let controller = currentWebViewController, controller.article != article {
|
||||
controller.setArticle(article)
|
||||
if let controller = currentWebViewController, controller.article != currentArticle {
|
||||
controller.setArticle(currentArticle)
|
||||
DispatchQueue.main.async {
|
||||
// You have to set the view controller to clear out the UIPageViewController child controller cache.
|
||||
// You also have to do it in an async call or you will get a strange assertion error.
|
||||
@ -52,9 +52,10 @@ class ArticleViewController: UIViewController, ArticleManager {
|
||||
view.bottomAnchor.constraint(equalTo: pageViewController.view.bottomAnchor)
|
||||
])
|
||||
|
||||
let controller = createWebViewController(article, updateView: true)
|
||||
let controller = createWebViewController(currentArticle, updateView: true)
|
||||
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
|
||||
|
||||
sceneModel?.updateArticleSelection()
|
||||
}
|
||||
|
||||
// MARK: API
|
||||
@ -97,7 +98,7 @@ extension ArticleViewController: UIPageViewControllerDataSource {
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
|
||||
guard let webViewController = viewController as? WebViewController,
|
||||
let currentArticle = webViewController.article,
|
||||
let article = articleModel?.findPrevArticle(currentArticle) else {
|
||||
let article = sceneModel?.findPrevArticle(currentArticle) else {
|
||||
return nil
|
||||
}
|
||||
return createWebViewController(article)
|
||||
@ -106,7 +107,7 @@ extension ArticleViewController: UIPageViewControllerDataSource {
|
||||
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
|
||||
guard let webViewController = viewController as? WebViewController,
|
||||
let currentArticle = webViewController.article,
|
||||
let article = articleModel?.findNextArticle(currentArticle) else {
|
||||
let article = sceneModel?.findNextArticle(currentArticle) else {
|
||||
return nil
|
||||
}
|
||||
return createWebViewController(article)
|
||||
@ -120,9 +121,9 @@ extension ArticleViewController: UIPageViewControllerDelegate {
|
||||
|
||||
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
|
||||
guard finished, completed else { return }
|
||||
guard let article = currentWebViewController?.article else { return }
|
||||
// guard let article = currentWebViewController?.article else { return }
|
||||
|
||||
articleModel?.selectArticle(article)
|
||||
sceneModel?.updateArticleSelection()
|
||||
// articleExtractorButton.buttonState = currentWebViewController?.articleExtractorButtonState ?? .off
|
||||
|
||||
previousViewControllers.compactMap({ $0 as? WebViewController }).forEach({ $0.stopWebViewActivity() })
|
||||
@ -136,15 +137,15 @@ private extension ArticleViewController {
|
||||
|
||||
func createWebViewController(_ article: Article?, updateView: Bool = true) -> WebViewController {
|
||||
let controller = WebViewController()
|
||||
controller.articleModel = articleModel
|
||||
controller.sceneModel = sceneModel
|
||||
controller.delegate = self
|
||||
controller.setArticle(article, updateView: updateView)
|
||||
return controller
|
||||
}
|
||||
|
||||
func resetWebViewController() {
|
||||
articleModel?.webViewProvider?.flushQueue()
|
||||
articleModel?.webViewProvider?.replenishQueueIfNeeded()
|
||||
sceneModel?.webViewProvider?.flushQueue()
|
||||
sceneModel?.webViewProvider?.replenishQueueIfNeeded()
|
||||
if let controller = currentWebViewController {
|
||||
controller.fullReload()
|
||||
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
|
||||
|
@ -57,7 +57,7 @@ class WebViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
var articleModel: ArticleModel?
|
||||
var sceneModel: SceneModel?
|
||||
weak var delegate: WebViewControllerDelegate?
|
||||
|
||||
private(set) var article: Article?
|
||||
@ -372,9 +372,10 @@ extension WebViewController: WKScriptMessageHandler {
|
||||
case MessageName.imageWasClicked:
|
||||
imageWasClicked(body: message.body as? String)
|
||||
case MessageName.showFeedInspector:
|
||||
if let webFeed = article?.webFeed {
|
||||
return
|
||||
// if let webFeed = article?.webFeed {
|
||||
// coordinator.showFeedInspector(for: webFeed)
|
||||
}
|
||||
// }
|
||||
default:
|
||||
return
|
||||
}
|
||||
@ -449,7 +450,7 @@ private extension WebViewController {
|
||||
return
|
||||
}
|
||||
|
||||
articleModel?.webViewProvider?.dequeueWebView() { webView in
|
||||
sceneModel?.webViewProvider?.dequeueWebView() { webView in
|
||||
|
||||
// Add the webview
|
||||
webView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -12,20 +12,17 @@ import Articles
|
||||
struct ArticleView: NSViewControllerRepresentable {
|
||||
|
||||
var sceneModel: SceneModel
|
||||
var articleModel: ArticleModel
|
||||
var article: Article
|
||||
|
||||
init(sceneModel: SceneModel, articleModel: ArticleModel, article: Article) {
|
||||
init(sceneModel: SceneModel, article: Article) {
|
||||
self.sceneModel = sceneModel
|
||||
self.articleModel = articleModel
|
||||
self.article = article
|
||||
sceneModel.articleModel = articleModel
|
||||
articleModel.delegate = sceneModel
|
||||
}
|
||||
|
||||
func makeNSViewController(context: Context) -> WebViewController {
|
||||
let controller = WebViewController()
|
||||
controller.articleModel = articleModel
|
||||
sceneModel.articleManager = controller
|
||||
controller.sceneModel = sceneModel
|
||||
controller.currentArticle = article
|
||||
return controller
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ class WebViewController: NSViewController, ArticleManager {
|
||||
}
|
||||
}
|
||||
|
||||
var articleModel: ArticleModel?
|
||||
var sceneModel: SceneModel?
|
||||
weak var delegate: WebViewControllerDelegate?
|
||||
|
||||
var currentArticle: Article?
|
||||
@ -75,6 +75,8 @@ class WebViewController: NSViewController, ArticleManager {
|
||||
])
|
||||
|
||||
loadWebView()
|
||||
|
||||
sceneModel?.updateArticleSelection()
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
@ -217,7 +219,7 @@ private extension WebViewController {
|
||||
return
|
||||
}
|
||||
|
||||
articleModel?.webViewProvider?.dequeueWebView() { webView in
|
||||
sceneModel?.webViewProvider?.dequeueWebView() { webView in
|
||||
|
||||
// Add the webview
|
||||
webView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
@ -211,6 +211,8 @@
|
||||
51707439232AA97100A461A3 /* ShareFolderPickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51707438232AA97100A461A3 /* ShareFolderPickerController.swift */; };
|
||||
5171B4D424B7BABA00FB8D3B /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
||||
5171B4F624B7BABA00FB8D3B /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
||||
5171B4F824B7CB3600FB8D3B /* ArticleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5171B4F724B7CB3600FB8D3B /* ArticleManager.swift */; };
|
||||
5171B4F924B7CB3600FB8D3B /* ArticleManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5171B4F724B7CB3600FB8D3B /* ArticleManager.swift */; };
|
||||
517630042336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; };
|
||||
517630052336215100E15FFF /* main.js in Resources */ = {isa = PBXBuildFile; fileRef = 517630032336215100E15FFF /* main.js */; };
|
||||
517630232336657E00E15FFF /* WebViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 517630222336657E00E15FFF /* WebViewProvider.swift */; };
|
||||
@ -312,8 +314,6 @@
|
||||
51A169A0235E10D700EB091F /* FeedbinAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */; };
|
||||
51A5769624AE617200078888 /* ArticleContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A5769524AE617200078888 /* ArticleContainerView.swift */; };
|
||||
51A5769724AE617200078888 /* ArticleContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A5769524AE617200078888 /* ArticleContainerView.swift */; };
|
||||
51A576BB24AE621800078888 /* ArticleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A576BA24AE621800078888 /* ArticleModel.swift */; };
|
||||
51A576BC24AE621800078888 /* ArticleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A576BA24AE621800078888 /* ArticleModel.swift */; };
|
||||
51A66685238075AE00CB272D /* AddWebFeedDefaultContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */; };
|
||||
51A9A5E12380C4FE0033AADF /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45255226507D200C03939 /* AppDefaults.swift */; };
|
||||
51A9A5E42380C8880033AADF /* ShareFolderPickerAccountCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */; };
|
||||
@ -1906,6 +1906,7 @@
|
||||
516AE9B22371C372007DEEAA /* MasterFeedTableViewSectionHeaderLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedTableViewSectionHeaderLayout.swift; sourceTree = "<group>"; };
|
||||
516AE9DE2372269A007DEEAA /* IconImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconImage.swift; sourceTree = "<group>"; };
|
||||
51707438232AA97100A461A3 /* ShareFolderPickerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareFolderPickerController.swift; sourceTree = "<group>"; };
|
||||
5171B4F724B7CB3600FB8D3B /* ArticleManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleManager.swift; sourceTree = "<group>"; };
|
||||
517630032336215100E15FFF /* main.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = main.js; sourceTree = "<group>"; };
|
||||
517630222336657E00E15FFF /* WebViewProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewProvider.swift; sourceTree = "<group>"; };
|
||||
5177470224B2657F00EB0F74 /* TimelineToolbarModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineToolbarModifier.swift; sourceTree = "<group>"; };
|
||||
@ -1976,7 +1977,6 @@
|
||||
51A16995235E10D600EB091F /* AboutViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = "<group>"; };
|
||||
51A5769524AE617200078888 /* ArticleContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleContainerView.swift; sourceTree = "<group>"; };
|
||||
51A576BA24AE621800078888 /* ArticleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleModel.swift; sourceTree = "<group>"; };
|
||||
51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedDefaultContainer.swift; sourceTree = "<group>"; };
|
||||
51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerAccountCell.xib; sourceTree = "<group>"; };
|
||||
51A9A5E52380C8B20033AADF /* ShareFolderPickerFolderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerFolderCell.xib; sourceTree = "<group>"; };
|
||||
@ -2863,11 +2863,11 @@
|
||||
5177472124B38CAE00EB0F74 /* ArticleButtonState.swift */,
|
||||
51A5769524AE617200078888 /* ArticleContainerView.swift */,
|
||||
5177471524B37D9700EB0F74 /* ArticleIconSchemeHandler.swift */,
|
||||
51A576BA24AE621800078888 /* ArticleModel.swift */,
|
||||
5177470524B2910300EB0F74 /* ArticleToolbarModifier.swift */,
|
||||
5177471324B37D4000EB0F74 /* PreloadedWebView.swift */,
|
||||
5177471924B3863000EB0F74 /* WebViewProvider.swift */,
|
||||
517B2EBB24B3E62A001AC46C /* WrapperScriptMessageHandler.swift */,
|
||||
5171B4F724B7CB3600FB8D3B /* ArticleManager.swift */,
|
||||
);
|
||||
path = Article;
|
||||
sourceTree = "<group>";
|
||||
@ -5085,7 +5085,6 @@
|
||||
51E4993C24A8709900B667CB /* AppDelegate.swift in Sources */,
|
||||
6591727F24B5D19500B638E8 /* SettingsDetailAccountView.swift in Sources */,
|
||||
51E498F924A8085D00B667CB /* SmartFeed.swift in Sources */,
|
||||
51A576BB24AE621800078888 /* ArticleModel.swift in Sources */,
|
||||
65ACE48824B48020003AE06A /* SettingsLocalAccountView.swift in Sources */,
|
||||
17930ED424AF10EE00A9BA52 /* AddWebFeedView.swift in Sources */,
|
||||
51E4995124A8734D00B667CB /* ExtensionPointManager.swift in Sources */,
|
||||
@ -5096,6 +5095,7 @@
|
||||
51E4990324A808BB00B667CB /* FaviconDownloader.swift in Sources */,
|
||||
172199ED24AB2E0100A31D04 /* SafariView.swift in Sources */,
|
||||
65ACE48624B477C9003AE06A /* SettingsAccountLabelView.swift in Sources */,
|
||||
5171B4F824B7CB3600FB8D3B /* ArticleManager.swift in Sources */,
|
||||
51E4990224A808BB00B667CB /* ColorHash.swift in Sources */,
|
||||
51919FAC24AA8CCA00541E64 /* UnreadCountView.swift in Sources */,
|
||||
5177476224B3BC4700EB0F74 /* SettingsAboutView.swift in Sources */,
|
||||
@ -5142,6 +5142,7 @@
|
||||
51E4996C24A8762D00B667CB /* ExtractedArticle.swift in Sources */,
|
||||
51E4990824A808C300B667CB /* RSHTMLMetadata+Extension.swift in Sources */,
|
||||
51919FF824AB8B7700541E64 /* TimelineView.swift in Sources */,
|
||||
5171B4F924B7CB3600FB8D3B /* ArticleManager.swift in Sources */,
|
||||
51E4992B24A8676300B667CB /* ArticleArray.swift in Sources */,
|
||||
51B54AB324B5AC830014348B /* ArticleButtonState.swift in Sources */,
|
||||
17D5F17224B0BC6700375168 /* SidebarToolbarModel.swift in Sources */,
|
||||
@ -5180,7 +5181,6 @@
|
||||
51919FB424AAB97900541E64 /* FeedIconImageLoader.swift in Sources */,
|
||||
51E4994A24A8734C00B667CB /* ExtensionPointManager.swift in Sources */,
|
||||
514E6C0324AD29A300AC6F6E /* TimelineItemStatusView.swift in Sources */,
|
||||
51A576BC24AE621800078888 /* ArticleModel.swift in Sources */,
|
||||
51B54A6524B549B20014348B /* WrapperScriptMessageHandler.swift in Sources */,
|
||||
51E4996D24A8762D00B667CB /* ArticleExtractor.swift in Sources */,
|
||||
51E4994024A8713B00B667CB /* AccountRefreshTimer.swift in Sources */,
|
||||
|
Loading…
x
Reference in New Issue
Block a user