// // 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 }