Refactor Styles to now be Themes
This commit is contained in:
parent
cfd2db58c5
commit
512e83d786
|
@ -16,6 +16,8 @@ enum FontSize: Int {
|
|||
|
||||
final class AppDefaults {
|
||||
|
||||
static let defaultThemeName = "Default"
|
||||
|
||||
static var shared = AppDefaults()
|
||||
private init() {}
|
||||
|
||||
|
@ -39,6 +41,7 @@ final class AppDefaults {
|
|||
static let importOPMLAccountID = "importOPMLAccountID"
|
||||
static let exportOPMLAccountID = "exportOPMLAccountID"
|
||||
static let defaultBrowserID = "defaultBrowserID"
|
||||
static let currentThemeName = "currentThemeName"
|
||||
|
||||
// Hidden prefs
|
||||
static let showDebugMenu = "ShowDebugMenu"
|
||||
|
@ -209,6 +212,15 @@ final class AppDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
var currentThemeName: String? {
|
||||
get {
|
||||
return AppDefaults.string(for: Key.currentThemeName)
|
||||
}
|
||||
set {
|
||||
AppDefaults.setString(for: Key.currentThemeName, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
var showTitleOnMainWindow: Bool {
|
||||
return AppDefaults.bool(for: Key.showTitleOnMainWindow)
|
||||
}
|
||||
|
@ -311,7 +323,8 @@ final class AppDefaults {
|
|||
Key.timelineGroupByFeed: false,
|
||||
"NSScrollViewShouldScrollUnderTitlebar": false,
|
||||
Key.refreshInterval: RefreshInterval.everyHour.rawValue,
|
||||
Key.showDebugMenu: showDebugMenu]
|
||||
Key.showDebugMenu: showDebugMenu,
|
||||
Key.currentThemeName: Self.defaultThemeName]
|
||||
|
||||
UserDefaults.standard.register(defaults: defaults)
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
|
||||
SecretsManager.provider = Secrets()
|
||||
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
||||
ArticleStylesManager.shared = ArticleStylesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Styles")!)
|
||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)
|
||||
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
|
|
|
@ -257,22 +257,22 @@ private extension DetailWebViewController {
|
|||
func reloadHTML() {
|
||||
delegate?.mouseDidExit(self)
|
||||
|
||||
let style = ArticleStylesManager.shared.currentStyle
|
||||
let theme = ArticleThemesManager.shared.currentTheme
|
||||
let rendering: ArticleRenderer.Rendering
|
||||
|
||||
switch state {
|
||||
case .noSelection:
|
||||
rendering = ArticleRenderer.noSelectionHTML(style: style)
|
||||
rendering = ArticleRenderer.noSelectionHTML(theme: theme)
|
||||
case .multipleSelection:
|
||||
rendering = ArticleRenderer.multipleSelectionHTML(style: style)
|
||||
rendering = ArticleRenderer.multipleSelectionHTML(theme: theme)
|
||||
case .loading:
|
||||
rendering = ArticleRenderer.loadingHTML(style: style)
|
||||
rendering = ArticleRenderer.loadingHTML(theme: theme)
|
||||
case .article(let article):
|
||||
detailIconSchemeHandler.currentArticle = article
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
case .extracted(let article, let extractedArticle):
|
||||
detailIconSchemeHandler.currentArticle = article
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, theme: theme)
|
||||
}
|
||||
|
||||
let substitutions = [
|
||||
|
|
|
@ -25,7 +25,7 @@ extension Article: PasteboardWriterOwner {
|
|||
static let articleUTIInternalType = NSPasteboard.PasteboardType(rawValue: articleUTIInternal)
|
||||
|
||||
private lazy var renderedHTML: String = {
|
||||
let rendering = ArticleRenderer.articleHTML(article: article, style: ArticleStylesManager.shared.currentStyle)
|
||||
let rendering = ArticleRenderer.articleHTML(article: article, theme: ArticleThemesManager.shared.currentTheme)
|
||||
return rendering.html
|
||||
}()
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
|
|||
|
||||
final class AppDefaults: ObservableObject {
|
||||
|
||||
static let defaultThemeName = "Default"
|
||||
|
||||
#if os(macOS)
|
||||
static let store: UserDefaults = UserDefaults.standard
|
||||
#endif
|
||||
|
@ -61,7 +63,8 @@ final class AppDefaults: ObservableObject {
|
|||
static let timelineGroupByFeed = "timelineGroupByFeed"
|
||||
static let timelineIconDimensions = "timelineIconDimensions"
|
||||
static let timelineNumberOfLines = "timelineNumberOfLines"
|
||||
|
||||
static let currentThemeName = "currentThemeName"
|
||||
|
||||
// Sidebar Defaults
|
||||
static let sidebarConfirmDelete = "sidebarConfirmDelete"
|
||||
|
||||
|
@ -242,6 +245,8 @@ final class AppDefaults: ObservableObject {
|
|||
var articleTextSize: ArticleTextSize {
|
||||
ArticleTextSize(rawValue: articleTextSizeTag) ?? ArticleTextSize.large
|
||||
}
|
||||
|
||||
@AppStorage(Key.currentThemeName, store: store) var currentThemeName: String?
|
||||
|
||||
// MARK: Refresh
|
||||
var lastRefresh: Date? {
|
||||
|
@ -334,7 +339,8 @@ final class AppDefaults: ObservableObject {
|
|||
Key.articleFullscreenEnabled: false,
|
||||
Key.confirmMarkAllAsRead: true,
|
||||
"NSScrollViewShouldScrollUnderTitlebar": false,
|
||||
Key.refreshInterval: RefreshInterval.everyHour.rawValue]
|
||||
Key.refreshInterval: RefreshInterval.everyHour.rawValue,
|
||||
Key.currentThemeName: Self.defaultThemeName]
|
||||
AppDefaults.store.register(defaults: defaults)
|
||||
}
|
||||
|
||||
|
|
|
@ -62,10 +62,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
appDelegate = self
|
||||
|
||||
SecretsManager.provider = Secrets()
|
||||
let documentAccountURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let documentAccountsFolder = documentAccountURL.appendingPathComponent("Accounts").absoluteString
|
||||
|
||||
let documentFolder = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
|
||||
let documentAccountsFolder = documentFolder.appendingPathComponent("Accounts").absoluteString
|
||||
let documentAccountsFolderPath = String(documentAccountsFolder.suffix(from: documentAccountsFolder.index(documentAccountsFolder.startIndex, offsetBy: 7)))
|
||||
AccountManager.shared = AccountManager(accountsFolder: documentAccountsFolderPath)
|
||||
|
||||
let documentStylesFolder = documentFolder.appendingPathComponent("Themes").absoluteString
|
||||
let documentStylesFolderPath = String(documentStylesFolder.suffix(from: documentAccountsFolder.index(documentStylesFolder.startIndex, offsetBy: 7)))
|
||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: documentStylesFolderPath)
|
||||
|
||||
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
|
|
|
@ -483,23 +483,23 @@ private extension WebViewController {
|
|||
func renderPage(_ webView: PreloadedWebView?) {
|
||||
guard let webView = webView else { return }
|
||||
|
||||
let style = ArticleStylesManager.shared.currentStyle
|
||||
let theme = ArticleThemesManager.shared.currentTheme
|
||||
let rendering: ArticleRenderer.Rendering
|
||||
|
||||
if let articleExtractor = articleExtractor, articleExtractor.state == .processing {
|
||||
rendering = ArticleRenderer.loadingHTML(style: style)
|
||||
rendering = ArticleRenderer.loadingHTML(theme: theme)
|
||||
} else if let articleExtractor = articleExtractor, articleExtractor.state == .failedToParse, let article = article {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
} else if let article = article, let extractedArticle = extractedArticle {
|
||||
if isShowingExtractedArticle {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, theme: theme)
|
||||
} else {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
}
|
||||
} else if let article = article {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
} else {
|
||||
rendering = ArticleRenderer.noSelectionHTML(style: style)
|
||||
rendering = ArticleRenderer.noSelectionHTML(theme: theme)
|
||||
}
|
||||
|
||||
let substitutions = [
|
||||
|
|
|
@ -72,6 +72,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDele
|
|||
|
||||
SecretsManager.provider = Secrets()
|
||||
AccountManager.shared = AccountManager(accountsFolder: Platform.dataSubfolder(forApplication: nil, folderName: "Accounts")!)
|
||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: Platform.dataSubfolder(forApplication: nil, folderName: "Themes")!)
|
||||
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||
|
|
|
@ -261,25 +261,25 @@ private extension WebViewController {
|
|||
}
|
||||
|
||||
func renderPage(_ webView: PreloadedWebView) {
|
||||
let style = ArticleStylesManager.shared.currentStyle
|
||||
let theme = ArticleThemesManager.shared.currentTheme
|
||||
let rendering: ArticleRenderer.Rendering
|
||||
|
||||
if articles?.count ?? 0 > 1 {
|
||||
rendering = ArticleRenderer.multipleSelectionHTML(style: style)
|
||||
rendering = ArticleRenderer.multipleSelectionHTML(theme: theme)
|
||||
} else if let articleExtractor = articleExtractor, articleExtractor.state == .processing {
|
||||
rendering = ArticleRenderer.loadingHTML(style: style)
|
||||
rendering = ArticleRenderer.loadingHTML(theme: theme)
|
||||
} else if let articleExtractor = articleExtractor, articleExtractor.state == .failedToParse, let article = articles?.first {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
} else if let article = articles?.first, let extractedArticle = extractedArticle {
|
||||
if isShowingExtractedArticle {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, theme: theme)
|
||||
} else {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
}
|
||||
} else if let article = articles?.first {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
} else {
|
||||
rendering = ArticleRenderer.noSelectionHTML(style: style)
|
||||
rendering = ArticleRenderer.noSelectionHTML(theme: theme)
|
||||
}
|
||||
|
||||
let substitutions = [
|
||||
|
|
|
@ -565,8 +565,8 @@
|
|||
51C45294226509C800C03939 /* SearchFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8477ACBD22238E9500DF7F37 /* SearchFeedDelegate.swift */; };
|
||||
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8444C8F11FED81840051386C /* OPMLExporter.swift */; };
|
||||
51C45297226509E300C03939 /* DefaultFeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */; };
|
||||
51C4529922650A0000C03939 /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; };
|
||||
51C4529A22650A0400C03939 /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
||||
51C4529922650A0000C03939 /* ArticleThemesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */; };
|
||||
51C4529A22650A0400C03939 /* ArticleTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleTheme.swift */; };
|
||||
51C4529B22650A1000C03939 /* FaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */; };
|
||||
51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29081FC74B8E007B49E3 /* SingleFaviconDownloader.swift */; };
|
||||
51C4529D22650A1000C03939 /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
|
||||
|
@ -736,10 +736,10 @@
|
|||
51E4996324A875F400B667CB /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
|
||||
51E4996424A875F400B667CB /* shared.css in Resources */ = {isa = PBXBuildFile; fileRef = B27EEBDF244D15F2000932E6 /* shared.css */; };
|
||||
51E4996524A875F400B667CB /* template.html in Resources */ = {isa = PBXBuildFile; fileRef = 848362FE2262A30E00DA1D35 /* template.html */; };
|
||||
51E4996624A8760B00B667CB /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
||||
51E4996724A8760B00B667CB /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; };
|
||||
51E4996824A8760C00B667CB /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
||||
51E4996924A8760C00B667CB /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; };
|
||||
51E4996624A8760B00B667CB /* ArticleTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleTheme.swift */; };
|
||||
51E4996724A8760B00B667CB /* ArticleThemesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */; };
|
||||
51E4996824A8760C00B667CB /* ArticleTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleTheme.swift */; };
|
||||
51E4996924A8760C00B667CB /* ArticleThemesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */; };
|
||||
51E4996A24A8762D00B667CB /* ExtractedArticle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FA73A62332BE880090D516 /* ExtractedArticle.swift */; };
|
||||
51E4996B24A8762D00B667CB /* ArticleExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FA73A32332BE110090D516 /* ArticleExtractor.swift */; };
|
||||
51E4996C24A8762D00B667CB /* ExtractedArticle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FA73A62332BE880090D516 /* ExtractedArticle.swift */; };
|
||||
|
@ -904,14 +904,14 @@
|
|||
65ED3FE5235DEF6C0081F399 /* DefaultFeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97591ED9EB0D007D329B /* DefaultFeedsImporter.swift */; };
|
||||
65ED3FE6235DEF6C0081F399 /* RenameWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A37CB4201ECD610087C5AF /* RenameWindowController.swift */; };
|
||||
65ED3FE7235DEF6C0081F399 /* SendToMicroBlogCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */; };
|
||||
65ED3FE8235DEF6C0081F399 /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
||||
65ED3FE8235DEF6C0081F399 /* ArticleTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleTheme.swift */; };
|
||||
65ED3FE9235DEF6C0081F399 /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; };
|
||||
65ED3FEA235DEF6C0081F399 /* SidebarViewController+ContextualMenus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B7178B201E66580091657D /* SidebarViewController+ContextualMenus.swift */; };
|
||||
65ED3FEC235DEF6C0081F399 /* RSHTMLMetadata+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842611A11FCB769D0086A189 /* RSHTMLMetadata+Extension.swift */; };
|
||||
65ED3FED235DEF6C0081F399 /* SendToMarsEditCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84A1500420048DDF0046AD9A /* SendToMarsEditCommand.swift */; };
|
||||
65ED3FEE235DEF6C0081F399 /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
|
||||
65ED3FEF235DEF6C0081F399 /* ScriptingObjectContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5907DB12004BB37005947E5 /* ScriptingObjectContainer.swift */; };
|
||||
65ED3FF0235DEF6C0081F399 /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; };
|
||||
65ED3FF0235DEF6C0081F399 /* ArticleThemesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */; };
|
||||
65ED3FF1235DEF6C0081F399 /* DetailContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8405DD892213E0E3008CE1BF /* DetailContainerView.swift */; };
|
||||
65ED3FF2235DEF6C0081F399 /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; };
|
||||
65ED3FF3235DEF6C0081F399 /* ArticleSorter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF3ABF1423259DDB0074C542 /* ArticleSorter.swift */; };
|
||||
|
@ -1101,8 +1101,8 @@
|
|||
849A977F1ED9EC42007D329B /* ArticleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A977D1ED9EC42007D329B /* ArticleRenderer.swift */; };
|
||||
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A977E1ED9EC42007D329B /* DetailViewController.swift */; };
|
||||
849A97831ED9EC63007D329B /* SidebarStatusBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97821ED9EC63007D329B /* SidebarStatusBarView.swift */; };
|
||||
849A97891ED9ECEF007D329B /* ArticleStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleStyle.swift */; };
|
||||
849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; };
|
||||
849A97891ED9ECEF007D329B /* ArticleTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97871ED9ECEF007D329B /* ArticleTheme.swift */; };
|
||||
849A978A1ED9ECEF007D329B /* ArticleThemesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */; };
|
||||
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; };
|
||||
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A979E1ED9F130007D329B /* SidebarCell.swift */; };
|
||||
849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; };
|
||||
|
@ -2021,8 +2021,8 @@
|
|||
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleRenderer.swift; sourceTree = "<group>"; };
|
||||
849A977E1ED9EC42007D329B /* DetailViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
|
||||
849A97821ED9EC63007D329B /* SidebarStatusBarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarStatusBarView.swift; sourceTree = "<group>"; };
|
||||
849A97871ED9ECEF007D329B /* ArticleStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleStyle.swift; sourceTree = "<group>"; };
|
||||
849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleStylesManager.swift; sourceTree = "<group>"; };
|
||||
849A97871ED9ECEF007D329B /* ArticleTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleTheme.swift; sourceTree = "<group>"; };
|
||||
849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleThemesManager.swift; sourceTree = "<group>"; };
|
||||
849A97971ED9EFAA007D329B /* Node-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Node-Extensions.swift"; sourceTree = "<group>"; };
|
||||
849A979E1ED9F130007D329B /* SidebarCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarCell.swift; sourceTree = "<group>"; };
|
||||
849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderTreeControllerDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -3437,8 +3437,8 @@
|
|||
849A97861ED9ECEF007D329B /* Article Styles */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
849A97871ED9ECEF007D329B /* ArticleStyle.swift */,
|
||||
849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */,
|
||||
849A97871ED9ECEF007D329B /* ArticleTheme.swift */,
|
||||
849A97881ED9ECEF007D329B /* ArticleThemesManager.swift */,
|
||||
);
|
||||
name = "Article Styles";
|
||||
path = Shared/ArticleStyles;
|
||||
|
@ -4961,7 +4961,7 @@
|
|||
51E4991724A8090400B667CB /* ArticleUtilities.swift in Sources */,
|
||||
51E4991B24A8091000B667CB /* IconImage.swift in Sources */,
|
||||
51E4995424A8734D00B667CB /* ExtensionPointIdentifer.swift in Sources */,
|
||||
51E4996924A8760C00B667CB /* ArticleStylesManager.swift in Sources */,
|
||||
51E4996924A8760C00B667CB /* ArticleThemesManager.swift in Sources */,
|
||||
5177471E24B387E100EB0F74 /* ImageTransition.swift in Sources */,
|
||||
51E498F324A8085D00B667CB /* PseudoFeed.swift in Sources */,
|
||||
172412A5257BC01C00ACCEBC /* AddCloudKitAccountView.swift in Sources */,
|
||||
|
@ -5005,7 +5005,7 @@
|
|||
172412AF257BC0C300ACCEBC /* AccountType+Helpers.swift in Sources */,
|
||||
51E4995624A8734D00B667CB /* TwitterFeedProvider-Extensions.swift in Sources */,
|
||||
5125E6CA24AE461D002A7562 /* TimelineLayoutView.swift in Sources */,
|
||||
51E4996824A8760C00B667CB /* ArticleStyle.swift in Sources */,
|
||||
51E4996824A8760C00B667CB /* ArticleTheme.swift in Sources */,
|
||||
51E4990024A808BB00B667CB /* FaviconGenerator.swift in Sources */,
|
||||
51E4997124A8764C00B667CB /* ActivityType.swift in Sources */,
|
||||
51E4991E24A8094300B667CB /* RSImage-AppIcons.swift in Sources */,
|
||||
|
@ -5096,7 +5096,7 @@
|
|||
51919FB024AA8EFA00541E64 /* SidebarItemView.swift in Sources */,
|
||||
1769E33624BD9621000E1E8E /* EditAccountCredentialsModel.swift in Sources */,
|
||||
51919FEF24AB85E400541E64 /* TimelineContainerView.swift in Sources */,
|
||||
51E4996624A8760B00B667CB /* ArticleStyle.swift in Sources */,
|
||||
51E4996624A8760B00B667CB /* ArticleTheme.swift in Sources */,
|
||||
5171B4D424B7BABA00FB8D3B /* MarkStatusCommand.swift in Sources */,
|
||||
17E4DBD624BFC53E00FE462A /* AdvancedPreferencesModel.swift in Sources */,
|
||||
5177470724B2910300EB0F74 /* ArticleToolbarModifier.swift in Sources */,
|
||||
|
@ -5180,7 +5180,7 @@
|
|||
51E498FB24A808BA00B667CB /* FaviconGenerator.swift in Sources */,
|
||||
17D5F19524B0C1DD00375168 /* SidebarToolbarModifier.swift in Sources */,
|
||||
17241288257BBF7000ACCEBC /* AddNewsBlurViewModel.swift in Sources */,
|
||||
51E4996724A8760B00B667CB /* ArticleStylesManager.swift in Sources */,
|
||||
51E4996724A8760B00B667CB /* ArticleThemesManager.swift in Sources */,
|
||||
1729529B24AA1FD200D65E66 /* MacSearchField.swift in Sources */,
|
||||
51408B7F24A9EC6F0073CF4E /* SidebarItem.swift in Sources */,
|
||||
514E6BDB24ACEA0400AC6F6E /* TimelineItemView.swift in Sources */,
|
||||
|
@ -5320,7 +5320,7 @@
|
|||
5193CD59245E44A90092735E /* RedditFeedProvider-Extensions.swift in Sources */,
|
||||
65ED3FE6235DEF6C0081F399 /* RenameWindowController.swift in Sources */,
|
||||
65ED3FE7235DEF6C0081F399 /* SendToMicroBlogCommand.swift in Sources */,
|
||||
65ED3FE8235DEF6C0081F399 /* ArticleStyle.swift in Sources */,
|
||||
65ED3FE8235DEF6C0081F399 /* ArticleTheme.swift in Sources */,
|
||||
65ED3FE9235DEF6C0081F399 /* FaviconURLFinder.swift in Sources */,
|
||||
6538131C2680E17F007A082C /* UserInfoKey.swift in Sources */,
|
||||
65ED3FEA235DEF6C0081F399 /* SidebarViewController+ContextualMenus.swift in Sources */,
|
||||
|
@ -5331,7 +5331,7 @@
|
|||
65ED3FEE235DEF6C0081F399 /* UserNotificationManager.swift in Sources */,
|
||||
653813182680E152007A082C /* AccountType+Helpers.swift in Sources */,
|
||||
65ED3FEF235DEF6C0081F399 /* ScriptingObjectContainer.swift in Sources */,
|
||||
65ED3FF0235DEF6C0081F399 /* ArticleStylesManager.swift in Sources */,
|
||||
65ED3FF0235DEF6C0081F399 /* ArticleThemesManager.swift in Sources */,
|
||||
65ED3FF1235DEF6C0081F399 /* DetailContainerView.swift in Sources */,
|
||||
65ED3FF2235DEF6C0081F399 /* SharingServiceDelegate.swift in Sources */,
|
||||
515A50E7243D07A90089E588 /* ExtensionPointManager.swift in Sources */,
|
||||
|
@ -5532,14 +5532,14 @@
|
|||
51FA73A82332BE880090D516 /* ExtractedArticle.swift in Sources */,
|
||||
51C4527C2265091600C03939 /* MasterTimelineDefaultCellLayout.swift in Sources */,
|
||||
51E4398023805EBC00015C31 /* AddComboTableViewCell.swift in Sources */,
|
||||
51C4529A22650A0400C03939 /* ArticleStyle.swift in Sources */,
|
||||
51C4529A22650A0400C03939 /* ArticleTheme.swift in Sources */,
|
||||
51C4527F2265092C00C03939 /* ArticleViewController.swift in Sources */,
|
||||
51C4526A226508F600C03939 /* MasterFeedTableViewCellLayout.swift in Sources */,
|
||||
51C452AE2265104D00C03939 /* ArticleStringFormatter.swift in Sources */,
|
||||
512E08E62268800D00BDCFDD /* FolderTreeControllerDelegate.swift in Sources */,
|
||||
51A9A60A2382FD240033AADF /* PoppableGestureRecognizerDelegate.swift in Sources */,
|
||||
51DC079A2552083500A3F79F /* ArticleTextSize.swift in Sources */,
|
||||
51C4529922650A0000C03939 /* ArticleStylesManager.swift in Sources */,
|
||||
51C4529922650A0000C03939 /* ArticleThemesManager.swift in Sources */,
|
||||
51EF0F802277A8330050506E /* MasterTimelineCellLayout.swift in Sources */,
|
||||
51F85BF722749FA100C787DC /* UIFont-Extensions.swift in Sources */,
|
||||
51C452AF2265108300C03939 /* ArticleArray.swift in Sources */,
|
||||
|
@ -5690,7 +5690,7 @@
|
|||
84A37CB5201ECD610087C5AF /* RenameWindowController.swift in Sources */,
|
||||
84A14FF320048CA70046AD9A /* SendToMicroBlogCommand.swift in Sources */,
|
||||
B2B8075E239C49D300F191E0 /* RSImage-AppIcons.swift in Sources */,
|
||||
849A97891ED9ECEF007D329B /* ArticleStyle.swift in Sources */,
|
||||
849A97891ED9ECEF007D329B /* ArticleTheme.swift in Sources */,
|
||||
84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */,
|
||||
84B7178C201E66580091657D /* SidebarViewController+ContextualMenus.swift in Sources */,
|
||||
842611A21FCB769D0086A189 /* RSHTMLMetadata+Extension.swift in Sources */,
|
||||
|
@ -5698,7 +5698,7 @@
|
|||
51FE10032345529D0056195D /* UserNotificationManager.swift in Sources */,
|
||||
D5907DB22004BB37005947E5 /* ScriptingObjectContainer.swift in Sources */,
|
||||
51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */,
|
||||
849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */,
|
||||
849A978A1ED9ECEF007D329B /* ArticleThemesManager.swift in Sources */,
|
||||
8405DD8A2213E0E3008CE1BF /* DetailContainerView.swift in Sources */,
|
||||
51107746243BEE2500D97C8C /* ExtensionPointPreferencesViewController.swift in Sources */,
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */,
|
||||
|
|
|
@ -37,15 +37,15 @@ struct ArticleRenderer {
|
|||
|
||||
private let article: Article?
|
||||
private let extractedArticle: ExtractedArticle?
|
||||
private let articleStyle: ArticleStyle
|
||||
private let articleTheme: ArticleTheme
|
||||
private let title: String
|
||||
private let body: String
|
||||
private let baseURL: String?
|
||||
|
||||
private init(article: Article?, extractedArticle: ExtractedArticle?, style: ArticleStyle) {
|
||||
private init(article: Article?, extractedArticle: ExtractedArticle?, theme: ArticleTheme) {
|
||||
self.article = article
|
||||
self.extractedArticle = extractedArticle
|
||||
self.articleStyle = style
|
||||
self.articleTheme = theme
|
||||
self.title = article?.sanitizedTitle() ?? ""
|
||||
if let content = extractedArticle?.content {
|
||||
self.body = content
|
||||
|
@ -58,28 +58,28 @@ struct ArticleRenderer {
|
|||
|
||||
// MARK: - API
|
||||
|
||||
static func articleHTML(article: Article, extractedArticle: ExtractedArticle? = nil, style: ArticleStyle) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: article, extractedArticle: extractedArticle, style: style)
|
||||
static func articleHTML(article: Article, extractedArticle: ExtractedArticle? = nil, theme: ArticleTheme) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: article, extractedArticle: extractedArticle, theme: theme)
|
||||
return (renderer.articleCSS, renderer.articleHTML, renderer.title, renderer.baseURL ?? "")
|
||||
}
|
||||
|
||||
static func multipleSelectionHTML(style: ArticleStyle) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, style: style)
|
||||
static func multipleSelectionHTML(theme: ArticleTheme) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, theme: theme)
|
||||
return (renderer.articleCSS, renderer.multipleSelectionHTML, renderer.title, renderer.baseURL ?? "")
|
||||
}
|
||||
|
||||
static func loadingHTML(style: ArticleStyle) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, style: style)
|
||||
static func loadingHTML(theme: ArticleTheme) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, theme: theme)
|
||||
return (renderer.articleCSS, renderer.loadingHTML, renderer.title, renderer.baseURL ?? "")
|
||||
}
|
||||
|
||||
static func noSelectionHTML(style: ArticleStyle) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, style: style)
|
||||
static func noSelectionHTML(theme: ArticleTheme) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, theme: theme)
|
||||
return (renderer.articleCSS, renderer.noSelectionHTML, renderer.title, renderer.baseURL ?? "")
|
||||
}
|
||||
|
||||
static func noContentHTML(style: ArticleStyle) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, style: style)
|
||||
static func noContentHTML(theme: ArticleTheme) -> Rendering {
|
||||
let renderer = ArticleRenderer(article: nil, extractedArticle: nil, theme: theme)
|
||||
return (renderer.articleCSS, renderer.noContentHTML, renderer.title, renderer.baseURL ?? "")
|
||||
}
|
||||
}
|
||||
|
@ -128,11 +128,11 @@ private extension ArticleRenderer {
|
|||
}()
|
||||
|
||||
func styleString() -> String {
|
||||
return articleStyle.css ?? ArticleRenderer.defaultStyleSheet
|
||||
return articleTheme.css ?? ArticleRenderer.defaultStyleSheet
|
||||
}
|
||||
|
||||
func template() -> String {
|
||||
return articleStyle.template ?? ArticleRenderer.defaultTemplate
|
||||
return articleTheme.template ?? ArticleRenderer.defaultTemplate
|
||||
}
|
||||
|
||||
func titleOrTitleLink() -> String {
|
||||
|
|
|
@ -1,187 +0,0 @@
|
|||
//
|
||||
// ArticleStylesManager.sqift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 9/26/15.
|
||||
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
import RSCore
|
||||
|
||||
let ArticleStyleNamesDidChangeNotification = "ArticleStyleNamesDidChangeNotification"
|
||||
let CurrentArticleStyleDidChangeNotification = "CurrentArticleStyleDidChangeNotification"
|
||||
|
||||
private let styleKey = "style"
|
||||
private let defaultStyleName = "Default"
|
||||
private let stylesInResourcesFolderName = "Styles"
|
||||
private let styleSuffix = ".netnewswirestyle"
|
||||
private let nnwStyleSuffix = ".nnwstyle"
|
||||
private let cssStyleSuffix = ".css"
|
||||
private let styleSuffixes = [styleSuffix, nnwStyleSuffix, cssStyleSuffix];
|
||||
|
||||
final class ArticleStylesManager {
|
||||
|
||||
static var shared: ArticleStylesManager!
|
||||
private let folderPath: String
|
||||
|
||||
var currentStyleName: String {
|
||||
get {
|
||||
return UserDefaults.standard.string(forKey: styleKey)!
|
||||
}
|
||||
set {
|
||||
if newValue != currentStyleName {
|
||||
UserDefaults.standard.set(newValue, forKey: styleKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentStyle: ArticleStyle {
|
||||
didSet {
|
||||
NotificationCenter.default.post(name: Notification.Name(rawValue: CurrentArticleStyleDidChangeNotification), object: self)
|
||||
}
|
||||
}
|
||||
|
||||
var styleNames = [defaultStyleName] {
|
||||
didSet {
|
||||
NotificationCenter.default.post(name: Notification.Name(rawValue: ArticleStyleNamesDidChangeNotification), object: self)
|
||||
}
|
||||
}
|
||||
|
||||
init(folderPath: String) {
|
||||
self.folderPath = folderPath
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: folderPath, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch {
|
||||
assertionFailure("Could not create folder for Styles.")
|
||||
abort()
|
||||
}
|
||||
|
||||
UserDefaults.standard.register(defaults: [styleKey: defaultStyleName])
|
||||
currentStyle = ArticleStyle.defaultStyle
|
||||
|
||||
updateStyleNames()
|
||||
updateCurrentStyle()
|
||||
|
||||
#if os(macOS)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: NSApplication.didBecomeActiveNotification, object: nil)
|
||||
#else
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc dynamic func applicationDidBecomeActive(_ note: Notification) {
|
||||
|
||||
updateStyleNames()
|
||||
updateCurrentStyle()
|
||||
}
|
||||
|
||||
// MARK : Internal
|
||||
|
||||
private func updateStyleNames() {
|
||||
|
||||
let updatedStyleNames = allStylePaths(folderPath).map { styleNameForPath($0) }
|
||||
|
||||
if updatedStyleNames != styleNames {
|
||||
styleNames = updatedStyleNames
|
||||
}
|
||||
}
|
||||
|
||||
private func articleStyleWithStyleName(_ styleName: String) -> ArticleStyle? {
|
||||
|
||||
if styleName == defaultStyleName {
|
||||
return ArticleStyle.defaultStyle
|
||||
}
|
||||
|
||||
guard let path = pathForStyleName(styleName, folder: folderPath) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ArticleStyle(path: path)
|
||||
}
|
||||
|
||||
private func defaultArticleStyle() -> ArticleStyle {
|
||||
|
||||
return articleStyleWithStyleName(defaultStyleName)!
|
||||
}
|
||||
|
||||
private func updateCurrentStyle() {
|
||||
|
||||
var styleName = currentStyleName
|
||||
if !styleNames.contains(styleName) {
|
||||
styleName = defaultStyleName
|
||||
currentStyleName = defaultStyleName
|
||||
}
|
||||
|
||||
var articleStyle = articleStyleWithStyleName(styleName)
|
||||
if articleStyle == nil {
|
||||
articleStyle = defaultArticleStyle()
|
||||
currentStyleName = defaultStyleName
|
||||
}
|
||||
|
||||
if let articleStyle = articleStyle, articleStyle != currentStyle {
|
||||
currentStyle = articleStyle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func allStylePaths(_ folder: String) -> [String] {
|
||||
|
||||
let filepaths = FileManager.default.filePaths(inFolder: folder)
|
||||
return filepaths?.filter { fileAtPathIsStyle($0) } ?? []
|
||||
}
|
||||
|
||||
private func fileAtPathIsStyle(_ f: String) -> Bool {
|
||||
|
||||
if !f.hasSuffix(styleSuffix) && !f.hasSuffix(nnwStyleSuffix) && !f.hasSuffix(cssStyleSuffix) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (f as NSString).lastPathComponent.hasPrefix(".") {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private func filenameWithStyleSuffixRemoved(_ filename: String) -> String {
|
||||
|
||||
for oneSuffix in styleSuffixes {
|
||||
if filename.hasSuffix(oneSuffix) {
|
||||
return filename.stripping(suffix: oneSuffix)
|
||||
}
|
||||
}
|
||||
|
||||
return filename
|
||||
}
|
||||
|
||||
private func styleNameForPath(_ f: String) -> String {
|
||||
|
||||
let filename = (f as NSString).lastPathComponent
|
||||
return filenameWithStyleSuffixRemoved(filename)
|
||||
}
|
||||
|
||||
private func pathIsPathForStyleName(_ styleName: String, path: String) -> Bool {
|
||||
|
||||
let filename = (path as NSString).lastPathComponent
|
||||
return filenameWithStyleSuffixRemoved(filename) == styleName
|
||||
}
|
||||
|
||||
private func pathForStyleName(_ styleName: String, folder: String) -> String? {
|
||||
for onePath in allStylePaths(folder) {
|
||||
if pathIsPathForStyleName(styleName, path: onePath) {
|
||||
return onePath
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// ArticleStyle.swift
|
||||
// ArticleTheme.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 9/26/15.
|
||||
|
@ -8,21 +8,18 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
struct ArticleStyle: Equatable {
|
||||
struct ArticleTheme: Equatable {
|
||||
|
||||
static let defaultStyle = ArticleStyle()
|
||||
static let defaultTheme = ArticleTheme()
|
||||
let path: String?
|
||||
let template: String?
|
||||
let css: String?
|
||||
let emptyCSS: String?
|
||||
let systemMessageCSS: String?
|
||||
let info: NSDictionary?
|
||||
|
||||
init() {
|
||||
|
||||
//Default style
|
||||
|
||||
self.path = nil;
|
||||
self.emptyCSS = nil
|
||||
self.systemMessageCSS = nil
|
||||
|
||||
self.info = ["CreatorHomePage": "https://netnewswire.com/", "CreatorName": "Ranchero Software", "Version": "1.0"]
|
||||
|
||||
|
@ -53,8 +50,8 @@ struct ArticleStyle: Equatable {
|
|||
let cssPath = (path as NSString).appendingPathComponent("stylesheet.css")
|
||||
self.css = stringAtPath(cssPath)
|
||||
|
||||
let emptyCSSPath = (path as NSString).appendingPathComponent("stylesheet_empty.css")
|
||||
self.emptyCSS = stringAtPath(emptyCSSPath)
|
||||
let systemMessageCSSPath = (path as NSString).appendingPathComponent("system_message_stylesheet.css")
|
||||
self.systemMessageCSS = stringAtPath(systemMessageCSSPath)
|
||||
|
||||
let templatePath = (path as NSString).appendingPathComponent("template.html")
|
||||
self.template = stringAtPath(templatePath)
|
||||
|
@ -64,7 +61,7 @@ struct ArticleStyle: Equatable {
|
|||
|
||||
self.css = stringAtPath(path)
|
||||
self.template = nil
|
||||
self.emptyCSS = nil
|
||||
self.systemMessageCSS = nil
|
||||
self.info = nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
//
|
||||
// ArticleThemesManager.sqift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 9/26/15.
|
||||
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
#if os(macOS)
|
||||
import AppKit
|
||||
#else
|
||||
import UIKit
|
||||
#endif
|
||||
|
||||
import RSCore
|
||||
|
||||
let ArticleThemeNamesDidChangeNotification = "ArticleThemeNamesDidChangeNotification"
|
||||
let CurrentArticleThemeDidChangeNotification = "CurrentArticleThemeDidChangeNotification"
|
||||
|
||||
private let themesInResourcesFolderName = "Themes"
|
||||
private let nnwThemeSuffix = ".nnwtheme"
|
||||
|
||||
final class ArticleThemesManager {
|
||||
|
||||
static var shared: ArticleThemesManager!
|
||||
private let folderPath: String
|
||||
|
||||
var currentThemeName: String {
|
||||
get {
|
||||
return AppDefaults.shared.currentThemeName ?? AppDefaults.defaultThemeName
|
||||
}
|
||||
set {
|
||||
if newValue != currentThemeName {
|
||||
AppDefaults.shared.currentThemeName = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var currentTheme: ArticleTheme {
|
||||
didSet {
|
||||
NotificationCenter.default.post(name: Notification.Name(rawValue: CurrentArticleThemeDidChangeNotification), object: self)
|
||||
}
|
||||
}
|
||||
|
||||
var themeNames = [AppDefaults.defaultThemeName] {
|
||||
didSet {
|
||||
NotificationCenter.default.post(name: Notification.Name(rawValue: ArticleThemeNamesDidChangeNotification), object: self)
|
||||
}
|
||||
}
|
||||
|
||||
init(folderPath: String) {
|
||||
self.folderPath = folderPath
|
||||
|
||||
do {
|
||||
try FileManager.default.createDirectory(atPath: folderPath, withIntermediateDirectories: true, attributes: nil)
|
||||
} catch {
|
||||
assertionFailure("Could not create folder for Themes.")
|
||||
abort()
|
||||
}
|
||||
|
||||
currentTheme = ArticleTheme.defaultTheme
|
||||
|
||||
updateThemeNames()
|
||||
updateCurrentTheme()
|
||||
|
||||
#if os(macOS)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: NSApplication.didBecomeActiveNotification, object: nil)
|
||||
#else
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil)
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
|
||||
@objc dynamic func applicationDidBecomeActive(_ note: Notification) {
|
||||
updateThemeNames()
|
||||
updateCurrentTheme()
|
||||
}
|
||||
|
||||
// MARK : Internal
|
||||
|
||||
private func updateThemeNames() {
|
||||
let updatedThemeNames = allThemePaths(folderPath).map { themeNameForPath($0) }
|
||||
|
||||
if updatedThemeNames != themeNames {
|
||||
themeNames = updatedThemeNames
|
||||
}
|
||||
}
|
||||
|
||||
private func articleThemeWithThemeName(_ themeName: String) -> ArticleTheme? {
|
||||
if themeName == AppDefaults.defaultThemeName {
|
||||
return ArticleTheme.defaultTheme
|
||||
}
|
||||
|
||||
guard let path = pathForThemeName(themeName, folder: folderPath) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ArticleTheme(path: path)
|
||||
}
|
||||
|
||||
private func defaultArticleTheme() -> ArticleTheme {
|
||||
return articleThemeWithThemeName(AppDefaults.defaultThemeName)!
|
||||
}
|
||||
|
||||
private func updateCurrentTheme() {
|
||||
|
||||
var themeName = currentThemeName
|
||||
if !themeNames.contains(themeName) {
|
||||
themeName = AppDefaults.defaultThemeName
|
||||
currentThemeName = AppDefaults.defaultThemeName
|
||||
}
|
||||
|
||||
var articleTheme = articleThemeWithThemeName(themeName)
|
||||
if articleTheme == nil {
|
||||
articleTheme = defaultArticleTheme()
|
||||
currentThemeName = AppDefaults.defaultThemeName
|
||||
}
|
||||
|
||||
if let articleTheme = articleTheme, articleTheme != currentTheme {
|
||||
currentTheme = articleTheme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func allThemePaths(_ folder: String) -> [String] {
|
||||
let filepaths = FileManager.default.filePaths(inFolder: folder)
|
||||
return filepaths?.filter { $0.hasSuffix(nnwThemeSuffix) } ?? []
|
||||
}
|
||||
|
||||
private func filenameWithThemeSuffixRemoved(_ filename: String) -> String {
|
||||
return filename.stripping(suffix: nnwThemeSuffix)
|
||||
}
|
||||
|
||||
private func themeNameForPath(_ f: String) -> String {
|
||||
let filename = (f as NSString).lastPathComponent
|
||||
return filenameWithThemeSuffixRemoved(filename)
|
||||
}
|
||||
|
||||
private func pathIsPathForThemeName(_ themeName: String, path: String) -> Bool {
|
||||
let filename = (path as NSString).lastPathComponent
|
||||
return filenameWithThemeSuffixRemoved(filename) == themeName
|
||||
}
|
||||
|
||||
private func pathForThemeName(_ themeName: String, folder: String) -> String? {
|
||||
for onePath in allThemePaths(folder) {
|
||||
if pathIsPathForThemeName(themeName, path: onePath) {
|
||||
return onePath
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -28,6 +28,8 @@ enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
|
|||
|
||||
final class AppDefaults {
|
||||
|
||||
static let defaultThemeName = "Defaults"
|
||||
|
||||
static let shared = AppDefaults()
|
||||
private init() {}
|
||||
|
||||
|
@ -55,6 +57,7 @@ final class AppDefaults {
|
|||
static let addWebFeedFolderName = "addWebFeedFolderName"
|
||||
static let addFolderAccountID = "addFolderAccountID"
|
||||
static let useSystemBrowser = "useSystemBrowser"
|
||||
static let currentThemeName = "currentThemeName"
|
||||
}
|
||||
|
||||
let isDeveloperBuild: Bool = {
|
||||
|
@ -220,6 +223,15 @@ final class AppDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
var currentThemeName: String? {
|
||||
get {
|
||||
return AppDefaults.string(for: Key.currentThemeName)
|
||||
}
|
||||
set {
|
||||
AppDefaults.setString(for: Key.currentThemeName, newValue)
|
||||
}
|
||||
}
|
||||
|
||||
static func registerDefaults() {
|
||||
let defaults: [String : Any] = [Key.userInterfaceColorPalette: UserInterfaceColorPalette.automatic.rawValue,
|
||||
Key.timelineGroupByFeed: false,
|
||||
|
@ -229,7 +241,8 @@ final class AppDefaults {
|
|||
Key.timelineSortDirection: ComparisonResult.orderedDescending.rawValue,
|
||||
Key.articleFullscreenAvailable: false,
|
||||
Key.articleFullscreenEnabled: false,
|
||||
Key.confirmMarkAllAsRead: true]
|
||||
Key.confirmMarkAllAsRead: true,
|
||||
Key.currentThemeName: Self.defaultThemeName]
|
||||
AppDefaults.store.register(defaults: defaults)
|
||||
}
|
||||
|
||||
|
|
|
@ -68,9 +68,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|||
let documentAccountsFolderPath = String(documentAccountsFolder.suffix(from: documentAccountsFolder.index(documentAccountsFolder.startIndex, offsetBy: 7)))
|
||||
AccountManager.shared = AccountManager(accountsFolder: documentAccountsFolderPath)
|
||||
|
||||
let documentStylesFolder = documentFolder.appendingPathComponent("Styles").absoluteString
|
||||
let documentStylesFolder = documentFolder.appendingPathComponent("Themes").absoluteString
|
||||
let documentStylesFolderPath = String(documentStylesFolder.suffix(from: documentAccountsFolder.index(documentStylesFolder.startIndex, offsetBy: 7)))
|
||||
ArticleStylesManager.shared = ArticleStylesManager(folderPath: documentStylesFolderPath)
|
||||
ArticleThemesManager.shared = ArticleThemesManager(folderPath: documentStylesFolderPath)
|
||||
|
||||
FeedProviderManager.shared.delegate = ExtensionPointManager.shared
|
||||
|
||||
|
|
|
@ -543,23 +543,23 @@ private extension WebViewController {
|
|||
func renderPage(_ webView: PreloadedWebView?) {
|
||||
guard let webView = webView else { return }
|
||||
|
||||
let style = ArticleStylesManager.shared.currentStyle
|
||||
let theme = ArticleThemesManager.shared.currentTheme
|
||||
let rendering: ArticleRenderer.Rendering
|
||||
|
||||
if let articleExtractor = articleExtractor, articleExtractor.state == .processing {
|
||||
rendering = ArticleRenderer.loadingHTML(style: style)
|
||||
rendering = ArticleRenderer.loadingHTML(theme: theme)
|
||||
} else if let articleExtractor = articleExtractor, articleExtractor.state == .failedToParse, let article = article {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
} else if let article = article, let extractedArticle = extractedArticle {
|
||||
if isShowingExtractedArticle {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, theme: theme)
|
||||
} else {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
}
|
||||
} else if let article = article {
|
||||
rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
} else {
|
||||
rendering = ArticleRenderer.noSelectionHTML(style: style)
|
||||
rendering = ArticleRenderer.noSelectionHTML(theme: theme)
|
||||
}
|
||||
|
||||
let substitutions = [
|
||||
|
|
Loading…
Reference in New Issue