Implement ArticleView for macOS
This commit is contained in:
parent
909dcb3f93
commit
6f055b5923
|
@ -14,9 +14,7 @@ import Account
|
||||||
import Articles
|
import Articles
|
||||||
|
|
||||||
protocol ArticleModelDelegate: class {
|
protocol ArticleModelDelegate: class {
|
||||||
#if os(iOS)
|
var articleModelWebViewProvider: WebViewProvider? { get }
|
||||||
var webViewProvider: WebViewProvider? { get }
|
|
||||||
#endif
|
|
||||||
func findPrevArticle(_: ArticleModel, article: Article) -> Article?
|
func findPrevArticle(_: ArticleModel, article: Article) -> Article?
|
||||||
func findNextArticle(_: ArticleModel, article: Article) -> Article?
|
func findNextArticle(_: ArticleModel, article: Article) -> Article?
|
||||||
func selectArticle(_: ArticleModel, article: Article)
|
func selectArticle(_: ArticleModel, article: Article)
|
||||||
|
@ -26,11 +24,9 @@ class ArticleModel: ObservableObject {
|
||||||
|
|
||||||
weak var delegate: ArticleModelDelegate?
|
weak var delegate: ArticleModelDelegate?
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
var webViewProvider: WebViewProvider? {
|
var webViewProvider: WebViewProvider? {
|
||||||
return delegate?.webViewProvider
|
return delegate?.articleModelWebViewProvider
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: API
|
// MARK: API
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,8 @@ class WebViewProvider: NSObject {
|
||||||
private let operationQueue = MainThreadOperationQueue()
|
private let operationQueue = MainThreadOperationQueue()
|
||||||
private var queue = NSMutableArray()
|
private var queue = NSMutableArray()
|
||||||
|
|
||||||
init(sceneModel: SceneModel) {
|
init(articleIconSchemeHandler: ArticleIconSchemeHandler) {
|
||||||
articleIconSchemeHandler = ArticleIconSchemeHandler(sceneModel: sceneModel)
|
self.articleIconSchemeHandler = articleIconSchemeHandler
|
||||||
super.init()
|
super.init()
|
||||||
replenishQueueIfNeeded()
|
replenishQueueIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,8 @@ final class SceneModel: ObservableObject {
|
||||||
var articleModel: ArticleModel?
|
var articleModel: ArticleModel?
|
||||||
|
|
||||||
private var refreshProgressModel: RefreshProgressModel? = nil
|
private var refreshProgressModel: RefreshProgressModel? = nil
|
||||||
private var _webViewProvider: WebViewProvider? = nil
|
private var articleIconSchemeHandler: ArticleIconSchemeHandler? = nil
|
||||||
|
private var webViewProvider: WebViewProvider? = nil
|
||||||
|
|
||||||
// MARK: API
|
// MARK: API
|
||||||
|
|
||||||
|
@ -27,7 +28,8 @@ final class SceneModel: ObservableObject {
|
||||||
self.refreshProgressModel = RefreshProgressModel()
|
self.refreshProgressModel = RefreshProgressModel()
|
||||||
self.refreshProgressModel!.$state.assign(to: self.$refreshProgressState)
|
self.refreshProgressModel!.$state.assign(to: self.$refreshProgressState)
|
||||||
|
|
||||||
self._webViewProvider = WebViewProvider(sceneModel: self)
|
self.articleIconSchemeHandler = ArticleIconSchemeHandler(sceneModel: self)
|
||||||
|
self.webViewProvider = WebViewProvider(articleIconSchemeHandler: self.articleIconSchemeHandler!)
|
||||||
}
|
}
|
||||||
|
|
||||||
func articleFor(_ articleID: String) -> Article? {
|
func articleFor(_ articleID: String) -> Article? {
|
||||||
|
@ -60,8 +62,8 @@ extension SceneModel: TimelineModelDelegate {
|
||||||
|
|
||||||
extension SceneModel: ArticleModelDelegate {
|
extension SceneModel: ArticleModelDelegate {
|
||||||
|
|
||||||
var webViewProvider: WebViewProvider? {
|
var articleModelWebViewProvider: WebViewProvider? {
|
||||||
return _webViewProvider
|
return webViewProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
func findPrevArticle(_: ArticleModel, article: Article) -> Article? {
|
func findPrevArticle(_: ArticleModel, article: Article) -> Article? {
|
||||||
|
|
|
@ -1,21 +1,37 @@
|
||||||
//
|
//
|
||||||
// ArticleView.swift
|
// ArticleView.swift
|
||||||
// NetNewsWire
|
// Multiplatform macOS
|
||||||
//
|
//
|
||||||
// Created by Maurice Parker on 7/2/20.
|
// Created by Maurice Parker on 7/8/20.
|
||||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Articles
|
import Articles
|
||||||
|
|
||||||
struct ArticleView: View {
|
final class ArticleView: NSViewControllerRepresentable {
|
||||||
|
|
||||||
var sceneModel: SceneModel
|
var sceneModel: SceneModel
|
||||||
var articleModel: ArticleModel
|
var articleModel: ArticleModel
|
||||||
var article: Article
|
var article: Article
|
||||||
|
|
||||||
var body: some View {
|
init(sceneModel: SceneModel, articleModel: ArticleModel, article: Article) {
|
||||||
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
|
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
|
||||||
|
controller.article = article
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateNSViewController(_ uiViewController: WebViewController, context: Context) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,330 @@
|
||||||
|
//
|
||||||
|
// WebViewController.swift
|
||||||
|
// Multiplatform macOS
|
||||||
|
//
|
||||||
|
// Created by Maurice Parker on 7/8/20.
|
||||||
|
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import Articles
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import RSCore
|
||||||
|
import Articles
|
||||||
|
|
||||||
|
extension Notification.Name {
|
||||||
|
static let appleColorPreferencesChangedNotification = Notification.Name("AppleColorPreferencesChangedNotification")
|
||||||
|
static let appleInterfaceThemeChangedNotification = Notification.Name("AppleInterfaceThemeChangedNotification")
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol WebViewControllerDelegate: class {
|
||||||
|
func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState)
|
||||||
|
}
|
||||||
|
|
||||||
|
class WebViewController: NSViewController {
|
||||||
|
|
||||||
|
private struct MessageName {
|
||||||
|
static let imageWasClicked = "imageWasClicked"
|
||||||
|
static let imageWasShown = "imageWasShown"
|
||||||
|
static let showFeedInspector = "showFeedInspector"
|
||||||
|
}
|
||||||
|
|
||||||
|
private var webView: PreloadedWebView?
|
||||||
|
|
||||||
|
private var articleExtractor: ArticleExtractor? = nil
|
||||||
|
var extractedArticle: ExtractedArticle?
|
||||||
|
var isShowingExtractedArticle = false
|
||||||
|
|
||||||
|
var articleExtractorButtonState: ArticleExtractorButtonState = .off {
|
||||||
|
didSet {
|
||||||
|
delegate?.webViewController(self, articleExtractorButtonStateDidUpdate: articleExtractorButtonState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var articleModel: ArticleModel?
|
||||||
|
weak var delegate: WebViewControllerDelegate?
|
||||||
|
|
||||||
|
var article: Article?
|
||||||
|
|
||||||
|
override func loadView() {
|
||||||
|
view = NSView()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||||
|
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleColorPreferencesChanged(_:)), name: .appleColorPreferencesChangedNotification, object: nil)
|
||||||
|
DistributedNotificationCenter.default().addObserver(self, selector: #selector(appleInterfaceThemeChanged(_:)), name: .appleInterfaceThemeChangedNotification, object: nil)
|
||||||
|
|
||||||
|
loadWebView()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Notifications
|
||||||
|
|
||||||
|
@objc func webFeedIconDidBecomeAvailable(_ note: Notification) {
|
||||||
|
reloadArticleImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func avatarDidBecomeAvailable(_ note: Notification) {
|
||||||
|
reloadArticleImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func faviconDidBecomeAvailable(_ note: Notification) {
|
||||||
|
reloadArticleImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func appleColorPreferencesChanged(_ note: Notification) {
|
||||||
|
loadWebView()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func appleInterfaceThemeChanged(_ note: Notification) {
|
||||||
|
loadWebView()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: API
|
||||||
|
|
||||||
|
func focus() {
|
||||||
|
webView?.becomeFirstResponder()
|
||||||
|
}
|
||||||
|
|
||||||
|
func canScrollDown(_ completion: @escaping (Bool) -> Void) {
|
||||||
|
fetchScrollInfo { (scrollInfo) in
|
||||||
|
completion(scrollInfo?.canScrollDown ?? false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func scrollPageDown(_ sender: Any?) {
|
||||||
|
webView?.scrollPageDown(sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggleArticleExtractor() {
|
||||||
|
|
||||||
|
guard let article = article else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard articleExtractor?.state != .processing else {
|
||||||
|
stopArticleExtractor()
|
||||||
|
loadWebView()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard !isShowingExtractedArticle else {
|
||||||
|
isShowingExtractedArticle = false
|
||||||
|
loadWebView()
|
||||||
|
articleExtractorButtonState = .off
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let articleExtractor = articleExtractor {
|
||||||
|
if article.preferredLink == articleExtractor.articleLink {
|
||||||
|
isShowingExtractedArticle = true
|
||||||
|
loadWebView()
|
||||||
|
articleExtractorButtonState = .on
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startArticleExtractor()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopArticleExtractorIfProcessing() {
|
||||||
|
if articleExtractor?.state == .processing {
|
||||||
|
stopArticleExtractor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopWebViewActivity() {
|
||||||
|
if let webView = webView {
|
||||||
|
stopMediaPlayback(webView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: ArticleExtractorDelegate
|
||||||
|
|
||||||
|
extension WebViewController: ArticleExtractorDelegate {
|
||||||
|
|
||||||
|
func articleExtractionDidFail(with: Error) {
|
||||||
|
stopArticleExtractor()
|
||||||
|
articleExtractorButtonState = .error
|
||||||
|
loadWebView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func articleExtractionDidComplete(extractedArticle: ExtractedArticle) {
|
||||||
|
if articleExtractor?.state != .cancelled {
|
||||||
|
self.extractedArticle = extractedArticle
|
||||||
|
isShowingExtractedArticle = true
|
||||||
|
loadWebView()
|
||||||
|
articleExtractorButtonState = .on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: WKScriptMessageHandler
|
||||||
|
|
||||||
|
extension WebViewController: WKScriptMessageHandler {
|
||||||
|
|
||||||
|
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
|
||||||
|
switch message.name {
|
||||||
|
case MessageName.imageWasShown:
|
||||||
|
return
|
||||||
|
case MessageName.imageWasClicked:
|
||||||
|
return
|
||||||
|
case MessageName.showFeedInspector:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Private
|
||||||
|
|
||||||
|
private extension WebViewController {
|
||||||
|
|
||||||
|
func loadWebView() {
|
||||||
|
if let webView = webView {
|
||||||
|
self.renderPage(webView)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
articleModel?.webViewProvider?.dequeueWebView() { webView in
|
||||||
|
|
||||||
|
// Add the webview
|
||||||
|
webView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
self.view.addSubview(webView)
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
self.view.leadingAnchor.constraint(equalTo: webView.leadingAnchor),
|
||||||
|
self.view.trailingAnchor.constraint(equalTo: webView.trailingAnchor),
|
||||||
|
self.view.topAnchor.constraint(equalTo: webView.topAnchor),
|
||||||
|
self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor)
|
||||||
|
])
|
||||||
|
|
||||||
|
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked)
|
||||||
|
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown)
|
||||||
|
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.showFeedInspector)
|
||||||
|
|
||||||
|
self.renderPage(webView)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderPage(_ webView: PreloadedWebView) {
|
||||||
|
let style = ArticleStylesManager.shared.currentStyle
|
||||||
|
let rendering: ArticleRenderer.Rendering
|
||||||
|
|
||||||
|
if let articleExtractor = articleExtractor, articleExtractor.state == .processing {
|
||||||
|
rendering = ArticleRenderer.loadingHTML(style: style)
|
||||||
|
} else if let articleExtractor = articleExtractor, articleExtractor.state == .failedToParse, let article = article {
|
||||||
|
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||||
|
} else if let article = article, let extractedArticle = extractedArticle {
|
||||||
|
if isShowingExtractedArticle {
|
||||||
|
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
|
||||||
|
} else {
|
||||||
|
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||||
|
}
|
||||||
|
} else if let article = article {
|
||||||
|
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||||
|
} else {
|
||||||
|
rendering = ArticleRenderer.noSelectionHTML(style: style)
|
||||||
|
}
|
||||||
|
|
||||||
|
let substitutions = [
|
||||||
|
"title": rendering.title,
|
||||||
|
"baseURL": rendering.baseURL,
|
||||||
|
"style": rendering.style,
|
||||||
|
"body": rendering.html
|
||||||
|
]
|
||||||
|
|
||||||
|
let html = try! MacroProcessor.renderedText(withTemplate: ArticleRenderer.page.html, substitutions: substitutions)
|
||||||
|
webView.loadHTMLString(html, baseURL: ArticleRenderer.page.baseURL)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchScrollInfo(_ completion: @escaping (ScrollInfo?) -> Void) {
|
||||||
|
guard let webView = webView else {
|
||||||
|
completion(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let javascriptString = "var x = {contentHeight: document.body.scrollHeight, offsetY: window.pageYOffset}; x"
|
||||||
|
|
||||||
|
webView.evaluateJavaScript(javascriptString) { (info, error) in
|
||||||
|
guard let info = info as? [String: Any] else {
|
||||||
|
completion(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let contentHeight = info["contentHeight"] as? CGFloat, let offsetY = info["offsetY"] as? CGFloat else {
|
||||||
|
completion(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let scrollInfo = ScrollInfo(contentHeight: contentHeight, viewHeight: webView.frame.height, offsetY: offsetY)
|
||||||
|
completion(scrollInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func startArticleExtractor() {
|
||||||
|
if let link = article?.preferredLink, let extractor = ArticleExtractor(link) {
|
||||||
|
extractor.delegate = self
|
||||||
|
extractor.process()
|
||||||
|
articleExtractor = extractor
|
||||||
|
articleExtractorButtonState = .animated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopArticleExtractor() {
|
||||||
|
articleExtractor?.cancel()
|
||||||
|
articleExtractor = nil
|
||||||
|
isShowingExtractedArticle = false
|
||||||
|
articleExtractorButtonState = .off
|
||||||
|
}
|
||||||
|
|
||||||
|
func reloadArticleImage() {
|
||||||
|
guard let article = article else { return }
|
||||||
|
|
||||||
|
var components = URLComponents()
|
||||||
|
components.scheme = ArticleRenderer.imageIconScheme
|
||||||
|
components.path = article.articleID
|
||||||
|
|
||||||
|
if let imageSrc = components.string {
|
||||||
|
webView?.evaluateJavaScript("reloadArticleImage(\"\(imageSrc)\")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stopMediaPlayback(_ webView: WKWebView) {
|
||||||
|
webView.evaluateJavaScript("stopMediaPlayback();")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ScrollInfo
|
||||||
|
|
||||||
|
private struct ScrollInfo {
|
||||||
|
|
||||||
|
let contentHeight: CGFloat
|
||||||
|
let viewHeight: CGFloat
|
||||||
|
let offsetY: CGFloat
|
||||||
|
let canScrollDown: Bool
|
||||||
|
let canScrollUp: Bool
|
||||||
|
|
||||||
|
init(contentHeight: CGFloat, viewHeight: CGFloat, offsetY: CGFloat) {
|
||||||
|
self.contentHeight = contentHeight
|
||||||
|
self.viewHeight = viewHeight
|
||||||
|
self.offsetY = offsetY
|
||||||
|
|
||||||
|
self.canScrollDown = viewHeight + offsetY < contentHeight
|
||||||
|
self.canScrollUp = offsetY > 0.1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -312,7 +312,6 @@
|
||||||
51A5769724AE617200078888 /* 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 */; };
|
51A576BB24AE621800078888 /* ArticleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A576BA24AE621800078888 /* ArticleModel.swift */; };
|
||||||
51A576BC24AE621800078888 /* ArticleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A576BA24AE621800078888 /* ArticleModel.swift */; };
|
51A576BC24AE621800078888 /* ArticleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A576BA24AE621800078888 /* ArticleModel.swift */; };
|
||||||
51A576BF24AE637400078888 /* ArticleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A576BD24AE637400078888 /* ArticleView.swift */; };
|
|
||||||
51A66685238075AE00CB272D /* AddWebFeedDefaultContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */; };
|
51A66685238075AE00CB272D /* AddWebFeedDefaultContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */; };
|
||||||
51A9A5E12380C4FE0033AADF /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45255226507D200C03939 /* AppDefaults.swift */; };
|
51A9A5E12380C4FE0033AADF /* AppDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51C45255226507D200C03939 /* AppDefaults.swift */; };
|
||||||
51A9A5E42380C8880033AADF /* ShareFolderPickerAccountCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */; };
|
51A9A5E42380C8880033AADF /* ShareFolderPickerAccountCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 51A9A5E32380C8870033AADF /* ShareFolderPickerAccountCell.xib */; };
|
||||||
|
@ -332,6 +331,8 @@
|
||||||
51B54A6724B549FE0014348B /* ArticleIconSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5177471524B37D9700EB0F74 /* ArticleIconSchemeHandler.swift */; };
|
51B54A6724B549FE0014348B /* ArticleIconSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5177471524B37D9700EB0F74 /* ArticleIconSchemeHandler.swift */; };
|
||||||
51B54A6924B54A490014348B /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B54A6824B54A490014348B /* IconView.swift */; };
|
51B54A6924B54A490014348B /* IconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B54A6824B54A490014348B /* IconView.swift */; };
|
||||||
51B54AB324B5AC830014348B /* ArticleExtractorButtonState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5177472124B38CAE00EB0F74 /* ArticleExtractorButtonState.swift */; };
|
51B54AB324B5AC830014348B /* ArticleExtractorButtonState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5177472124B38CAE00EB0F74 /* ArticleExtractorButtonState.swift */; };
|
||||||
|
51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B54AB524B5B33C0014348B /* WebViewController.swift */; };
|
||||||
|
51B54ABC24B5BEF20014348B /* ArticleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B54ABB24B5BEF20014348B /* ArticleView.swift */; };
|
||||||
51B5C87723F22B8200032075 /* ExtensionContainers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87623F22B8200032075 /* ExtensionContainers.swift */; };
|
51B5C87723F22B8200032075 /* ExtensionContainers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87623F22B8200032075 /* ExtensionContainers.swift */; };
|
||||||
51B5C87B23F2317700032075 /* ExtensionFeedAddRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */; };
|
51B5C87B23F2317700032075 /* ExtensionFeedAddRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */; };
|
||||||
51B5C87D23F2346200032075 /* ExtensionContainersFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87C23F2346200032075 /* ExtensionContainersFile.swift */; };
|
51B5C87D23F2346200032075 /* ExtensionContainersFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B5C87C23F2346200032075 /* ExtensionContainersFile.swift */; };
|
||||||
|
@ -1969,7 +1970,6 @@
|
||||||
51A16996235E10D700EB091F /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.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>"; };
|
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>"; };
|
51A576BA24AE621800078888 /* ArticleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleModel.swift; sourceTree = "<group>"; };
|
||||||
51A576BD24AE637400078888 /* ArticleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleView.swift; sourceTree = "<group>"; };
|
|
||||||
51A66684238075AE00CB272D /* AddWebFeedDefaultContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddWebFeedDefaultContainer.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>"; };
|
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>"; };
|
51A9A5E52380C8B20033AADF /* ShareFolderPickerFolderCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ShareFolderPickerFolderCell.xib; sourceTree = "<group>"; };
|
||||||
|
@ -1978,6 +1978,8 @@
|
||||||
51A9A6092382FD240033AADF /* PoppableGestureRecognizerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoppableGestureRecognizerDelegate.swift; sourceTree = "<group>"; };
|
51A9A6092382FD240033AADF /* PoppableGestureRecognizerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoppableGestureRecognizerDelegate.swift; sourceTree = "<group>"; };
|
||||||
51AB8AB223B7F4C6008F147D /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = "<group>"; };
|
51AB8AB223B7F4C6008F147D /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = "<group>"; };
|
||||||
51B54A6824B54A490014348B /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = "<group>"; };
|
51B54A6824B54A490014348B /* IconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IconView.swift; sourceTree = "<group>"; };
|
||||||
|
51B54AB524B5B33C0014348B /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = "<group>"; };
|
||||||
|
51B54ABB24B5BEF20014348B /* ArticleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleView.swift; sourceTree = "<group>"; };
|
||||||
51B5C87623F22B8200032075 /* ExtensionContainers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainers.swift; sourceTree = "<group>"; };
|
51B5C87623F22B8200032075 /* ExtensionContainers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainers.swift; sourceTree = "<group>"; };
|
||||||
51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionFeedAddRequest.swift; sourceTree = "<group>"; };
|
51B5C87A23F2317700032075 /* ExtensionFeedAddRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionFeedAddRequest.swift; sourceTree = "<group>"; };
|
||||||
51B5C87C23F2346200032075 /* ExtensionContainersFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainersFile.swift; sourceTree = "<group>"; };
|
51B5C87C23F2346200032075 /* ExtensionContainersFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionContainersFile.swift; sourceTree = "<group>"; };
|
||||||
|
@ -2730,8 +2732,9 @@
|
||||||
5177470C24B2FF3B00EB0F74 /* Article */ = {
|
5177470C24B2FF3B00EB0F74 /* Article */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
51A576BD24AE637400078888 /* ArticleView.swift */,
|
51B54AB524B5B33C0014348B /* WebViewController.swift */,
|
||||||
51B54A6824B54A490014348B /* IconView.swift */,
|
51B54A6824B54A490014348B /* IconView.swift */,
|
||||||
|
51B54ABB24B5BEF20014348B /* ArticleView.swift */,
|
||||||
);
|
);
|
||||||
path = Article;
|
path = Article;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -5104,6 +5107,7 @@
|
||||||
51E498FA24A808BA00B667CB /* SingleFaviconDownloader.swift in Sources */,
|
51E498FA24A808BA00B667CB /* SingleFaviconDownloader.swift in Sources */,
|
||||||
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
|
51E4993F24A8713B00B667CB /* ArticleStatusSyncTimer.swift in Sources */,
|
||||||
51E4993724A8680E00B667CB /* Reachability.swift in Sources */,
|
51E4993724A8680E00B667CB /* Reachability.swift in Sources */,
|
||||||
|
51B54AB624B5B33C0014348B /* WebViewController.swift in Sources */,
|
||||||
51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */,
|
51E4994B24A8734C00B667CB /* SendToMicroBlogCommand.swift in Sources */,
|
||||||
51E4996F24A8764C00B667CB /* ActivityType.swift in Sources */,
|
51E4996F24A8764C00B667CB /* ActivityType.swift in Sources */,
|
||||||
51E4994E24A8734C00B667CB /* SendToMarsEditCommand.swift in Sources */,
|
51E4994E24A8734C00B667CB /* SendToMarsEditCommand.swift in Sources */,
|
||||||
|
@ -5188,6 +5192,7 @@
|
||||||
51919FA724AA64B000541E64 /* SidebarView.swift in Sources */,
|
51919FA724AA64B000541E64 /* SidebarView.swift in Sources */,
|
||||||
51E498FD24A808BA00B667CB /* ColorHash.swift in Sources */,
|
51E498FD24A808BA00B667CB /* ColorHash.swift in Sources */,
|
||||||
51E4991824A8090A00B667CB /* CacheCleaner.swift in Sources */,
|
51E4991824A8090A00B667CB /* CacheCleaner.swift in Sources */,
|
||||||
|
51B54ABC24B5BEF20014348B /* ArticleView.swift in Sources */,
|
||||||
51E498CD24A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
|
51E498CD24A8085D00B667CB /* SearchTimelineFeedDelegate.swift in Sources */,
|
||||||
51E4996124A875F400B667CB /* ArticleRenderer.swift in Sources */,
|
51E4996124A875F400B667CB /* ArticleRenderer.swift in Sources */,
|
||||||
FA80C13F24B072AB00974098 /* AddFolderModel.swift in Sources */,
|
FA80C13F24B072AB00974098 /* AddFolderModel.swift in Sources */,
|
||||||
|
@ -5202,7 +5207,6 @@
|
||||||
51E4992D24A8676300B667CB /* FetchRequestOperation.swift in Sources */,
|
51E4992D24A8676300B667CB /* FetchRequestOperation.swift in Sources */,
|
||||||
51E4992424A8098400B667CB /* SmartFeedPasteboardWriter.swift in Sources */,
|
51E4992424A8098400B667CB /* SmartFeedPasteboardWriter.swift in Sources */,
|
||||||
51E4991424A808FF00B667CB /* ArticleStringFormatter.swift in Sources */,
|
51E4991424A808FF00B667CB /* ArticleStringFormatter.swift in Sources */,
|
||||||
51A576BF24AE637400078888 /* ArticleView.swift in Sources */,
|
|
||||||
FF64D0EA24AF53EE0084080A /* RefreshProgressView.swift in Sources */,
|
FF64D0EA24AF53EE0084080A /* RefreshProgressView.swift in Sources */,
|
||||||
51B54A6624B549CB0014348B /* PreloadedWebView.swift in Sources */,
|
51B54A6624B549CB0014348B /* PreloadedWebView.swift in Sources */,
|
||||||
51E4991024A808DE00B667CB /* SmallIconProvider.swift in Sources */,
|
51E4991024A808DE00B667CB /* SmallIconProvider.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue