Clean up TimelineStringUtilities, rename to TimelineStringFormatter.
This commit is contained in:
parent
119a854ef0
commit
9e941cfc9f
|
@ -79,7 +79,7 @@
|
|||
849A97761ED9EC04007D329B /* TimelineCellAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97701ED9EC04007D329B /* TimelineCellAppearance.swift */; };
|
||||
849A97771ED9EC04007D329B /* TimelineCellData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97711ED9EC04007D329B /* TimelineCellData.swift */; };
|
||||
849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97721ED9EC04007D329B /* TimelineCellLayout.swift */; };
|
||||
849A97791ED9EC04007D329B /* TimelineStringUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97731ED9EC04007D329B /* TimelineStringUtilities.swift */; };
|
||||
849A97791ED9EC04007D329B /* TimelineStringFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97731ED9EC04007D329B /* TimelineStringFormatter.swift */; };
|
||||
849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97741ED9EC04007D329B /* TimelineTableCellView.swift */; };
|
||||
849A977B1ED9EC04007D329B /* UnreadIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97751ED9EC04007D329B /* UnreadIndicatorView.swift */; };
|
||||
849A977F1ED9EC42007D329B /* ArticleRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A977D1ED9EC42007D329B /* ArticleRenderer.swift */; };
|
||||
|
@ -557,7 +557,7 @@
|
|||
849A97701ED9EC04007D329B /* TimelineCellAppearance.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineCellAppearance.swift; sourceTree = "<group>"; };
|
||||
849A97711ED9EC04007D329B /* TimelineCellData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineCellData.swift; sourceTree = "<group>"; };
|
||||
849A97721ED9EC04007D329B /* TimelineCellLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineCellLayout.swift; sourceTree = "<group>"; };
|
||||
849A97731ED9EC04007D329B /* TimelineStringUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineStringUtilities.swift; sourceTree = "<group>"; };
|
||||
849A97731ED9EC04007D329B /* TimelineStringFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineStringFormatter.swift; sourceTree = "<group>"; };
|
||||
849A97741ED9EC04007D329B /* TimelineTableCellView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimelineTableCellView.swift; sourceTree = "<group>"; };
|
||||
849A97751ED9EC04007D329B /* UnreadIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnreadIndicatorView.swift; sourceTree = "<group>"; };
|
||||
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleRenderer.swift; sourceTree = "<group>"; };
|
||||
|
@ -1013,7 +1013,7 @@
|
|||
84E185B2203B74E500F69BFA /* SingleLineTextFieldSizer.swift */,
|
||||
84E185C2203BB12600F69BFA /* MultilineTextFieldSizer.swift */,
|
||||
849A97711ED9EC04007D329B /* TimelineCellData.swift */,
|
||||
849A97731ED9EC04007D329B /* TimelineStringUtilities.swift */,
|
||||
849A97731ED9EC04007D329B /* TimelineStringFormatter.swift */,
|
||||
849A97751ED9EC04007D329B /* UnreadIndicatorView.swift */,
|
||||
);
|
||||
path = Cell;
|
||||
|
@ -1950,7 +1950,7 @@
|
|||
849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */,
|
||||
519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */,
|
||||
84E8E0DB202EC49300562D8F /* TimelineViewController+ContextualMenus.swift in Sources */,
|
||||
849A97791ED9EC04007D329B /* TimelineStringUtilities.swift in Sources */,
|
||||
849A97791ED9EC04007D329B /* TimelineStringFormatter.swift in Sources */,
|
||||
84E185C3203BB12600F69BFA /* MultilineTextFieldSizer.swift in Sources */,
|
||||
8472058120142E8900AD578B /* FeedInspectorViewController.swift in Sources */,
|
||||
84AD1EAA2031617300BC20B7 /* FolderPasteboardWriter.swift in Sources */,
|
||||
|
|
|
@ -180,7 +180,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
|
|||
|
||||
func applicationDidResignActive(_ notification: Notification) {
|
||||
|
||||
timelineEmptyCaches()
|
||||
TimelineStringFormatter.emptyCaches()
|
||||
|
||||
saveState()
|
||||
}
|
||||
|
|
|
@ -24,13 +24,13 @@ struct TimelineCellData {
|
|||
|
||||
init(article: Article, appearance: TimelineCellAppearance, showFeedName: Bool, feedName: String?, avatar: NSImage?, showAvatar: Bool, featuredImage: NSImage?) {
|
||||
|
||||
self.title = timelineTruncatedTitle(article)
|
||||
self.text = timelineTruncatedSummary(article)
|
||||
self.title = TimelineStringFormatter.truncatedTitle(article)
|
||||
self.text = TimelineStringFormatter.truncatedSummary(article)
|
||||
|
||||
self.dateString = timelineDateString(article.logicalDatePublished)
|
||||
self.dateString = TimelineStringFormatter.dateString(article.logicalDatePublished)
|
||||
|
||||
if let feedName = feedName {
|
||||
self.feedName = timelineTruncatedFeedName(feedName)
|
||||
self.feedName = TimelineStringFormatter.truncatedFeedName(feedName)
|
||||
}
|
||||
else {
|
||||
self.feedName = ""
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
//
|
||||
// TimelineStringFormatter.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 8/31/15.
|
||||
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Articles
|
||||
import RSParser
|
||||
|
||||
class TimelineStringFormatter {
|
||||
|
||||
private static var feedNameCache = [String: String]()
|
||||
private static var titleCache = [String: String]()
|
||||
private static var summaryCache = [String: String]()
|
||||
|
||||
private static let dateFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .none
|
||||
return formatter
|
||||
}()
|
||||
|
||||
private static let timeFormatter: DateFormatter = {
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .none
|
||||
formatter.timeStyle = .short
|
||||
return formatter
|
||||
}()
|
||||
|
||||
static func emptyCaches() {
|
||||
feedNameCache = [String: String]()
|
||||
titleCache = [String: String]()
|
||||
summaryCache = [String: String]()
|
||||
}
|
||||
|
||||
static func truncatedFeedName(_ feedName: String) -> String {
|
||||
if let cachedFeedName = feedNameCache[feedName] {
|
||||
return cachedFeedName
|
||||
}
|
||||
|
||||
let maxFeedNameLength = 100
|
||||
if feedName.count < maxFeedNameLength {
|
||||
feedNameCache[feedName] = feedName
|
||||
return feedName
|
||||
}
|
||||
|
||||
let s = (feedName as NSString).substring(to: maxFeedNameLength)
|
||||
feedNameCache[feedName] = s
|
||||
return s
|
||||
}
|
||||
|
||||
static func truncatedTitle(_ article: Article) -> String {
|
||||
guard let title = article.title else {
|
||||
return ""
|
||||
}
|
||||
|
||||
if let cachedTitle = titleCache[title] {
|
||||
return cachedTitle
|
||||
}
|
||||
|
||||
var s = title.replacingOccurrences(of: "\n", with: "")
|
||||
s = s.replacingOccurrences(of: "\r", with: "")
|
||||
s = s.replacingOccurrences(of: "\t", with: "")
|
||||
s = s.rsparser_stringByDecodingHTMLEntities()
|
||||
s = s.replacingOccurrences(of: "↦", with: "")
|
||||
s = s.rs_stringByTrimmingWhitespace()
|
||||
s = s.rs_stringWithCollapsedWhitespace()
|
||||
|
||||
let maxLength = 1000
|
||||
if s.count < maxLength {
|
||||
titleCache[title] = s
|
||||
return s
|
||||
}
|
||||
|
||||
s = (s as NSString).substring(to: maxLength)
|
||||
titleCache[title] = s
|
||||
return s
|
||||
}
|
||||
|
||||
static func truncatedSummary(_ article: Article) -> String {
|
||||
guard let body = article.body else {
|
||||
return ""
|
||||
}
|
||||
|
||||
if let cachedBody = summaryCache[body] {
|
||||
return cachedBody
|
||||
}
|
||||
var s = body.rsparser_stringByDecodingHTMLEntities()
|
||||
s = s.rs_string(byStrippingHTML: 300)
|
||||
s = s.rs_stringByTrimmingWhitespace()
|
||||
s = s.rs_stringWithCollapsedWhitespace()
|
||||
if s == "Comments" { // Hacker News.
|
||||
s = ""
|
||||
}
|
||||
summaryCache[body] = s
|
||||
return s
|
||||
}
|
||||
|
||||
static func dateString(_ date: Date) -> String {
|
||||
if NSCalendar.rs_dateIsToday(date) {
|
||||
return timeFormatter.string(from: date)
|
||||
}
|
||||
return dateFormatter.string(from: date)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
//
|
||||
// TimelineStringUtilities.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 8/31/15.
|
||||
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Articles
|
||||
import RSParser
|
||||
|
||||
// TODO: Don’t make all this at top level.
|
||||
|
||||
private var truncatedFeedNameCache = [String: String]()
|
||||
private let truncatedTitleCache = NSMutableDictionary()
|
||||
private let normalizedTextCache = NSMutableDictionary()
|
||||
private let textCache = NSMutableDictionary()
|
||||
private let summaryCache = NSMutableDictionary()
|
||||
|
||||
func timelineEmptyCaches() {
|
||||
|
||||
truncatedFeedNameCache = [String: String]()
|
||||
truncatedTitleCache.removeAllObjects()
|
||||
normalizedTextCache.removeAllObjects()
|
||||
textCache.removeAllObjects()
|
||||
summaryCache.removeAllObjects()
|
||||
}
|
||||
|
||||
func timelineTruncatedFeedName(_ feedName: String) -> String {
|
||||
|
||||
if let cachedFeedName = truncatedFeedNameCache[feedName] {
|
||||
return cachedFeedName
|
||||
}
|
||||
|
||||
let maxFeedNameLength = 100
|
||||
if feedName.count < maxFeedNameLength {
|
||||
truncatedFeedNameCache[feedName] = feedName
|
||||
return feedName
|
||||
}
|
||||
|
||||
let s = (feedName as NSString).substring(to: maxFeedNameLength)
|
||||
truncatedFeedNameCache[feedName] = s
|
||||
return s
|
||||
}
|
||||
|
||||
func timelineTruncatedTitle(_ article: Article) -> String {
|
||||
|
||||
guard let title = article.title else {
|
||||
return ""
|
||||
}
|
||||
|
||||
if let cachedTitle = truncatedTitleCache[title] as? String {
|
||||
return cachedTitle
|
||||
}
|
||||
|
||||
var s = title.replacingOccurrences(of: "\n", with: "")
|
||||
s = s.replacingOccurrences(of: "\r", with: "")
|
||||
s = s.replacingOccurrences(of: "\t", with: "")
|
||||
s = s.replacingOccurrences(of: "↦", with: "")
|
||||
s = s.rs_stringByTrimmingWhitespace()
|
||||
|
||||
let maxLength = 1000
|
||||
if s.count < maxLength {
|
||||
truncatedTitleCache[title] = s
|
||||
return s
|
||||
}
|
||||
|
||||
s = (s as NSString).substring(to: maxLength)
|
||||
truncatedTitleCache[title] = s
|
||||
return s
|
||||
}
|
||||
|
||||
func timelineTruncatedSummary(_ article: Article) -> String {
|
||||
|
||||
return timelineSummaryForArticle(article)
|
||||
}
|
||||
|
||||
func timelineNormalizedText(_ text: String) -> String {
|
||||
|
||||
if text.isEmpty {
|
||||
return ""
|
||||
}
|
||||
if let cachedText = normalizedTextCache[text] as? String {
|
||||
return cachedText
|
||||
}
|
||||
|
||||
var s = (text as NSString).rs_stringByTrimmingWhitespace()
|
||||
s = s.rs_stringWithCollapsedWhitespace()
|
||||
|
||||
let result = s as String
|
||||
normalizedTextCache[text] = result
|
||||
return result
|
||||
}
|
||||
|
||||
func timelineNormalizedTextTruncated(_ text: String) -> String {
|
||||
|
||||
if text.isEmpty {
|
||||
return ""
|
||||
}
|
||||
|
||||
if let cachedText = textCache[text] as? String {
|
||||
return cachedText
|
||||
}
|
||||
|
||||
var s: NSString = (text as NSString).rsparser_stringByDecodingHTMLEntities() as NSString
|
||||
s = s.rs_stringByTrimmingWhitespace() as NSString
|
||||
s = s.rs_stringWithCollapsedWhitespace() as NSString
|
||||
|
||||
let maxLength = 512
|
||||
if s.length > maxLength {
|
||||
s = s.substring(to: maxLength) as NSString
|
||||
}
|
||||
|
||||
textCache[text] = String(s)
|
||||
return s as String
|
||||
}
|
||||
|
||||
|
||||
func timelineSummaryForArticle(_ article: Article) -> String {
|
||||
|
||||
guard let body = article.body else {
|
||||
return ""
|
||||
}
|
||||
|
||||
if let cachedBody = summaryCache[body] as? String {
|
||||
return cachedBody
|
||||
}
|
||||
|
||||
var s = body.rs_string(byStrippingHTML: 300)
|
||||
s = timelineNormalizedText(s)
|
||||
if s == "Comments" { // Hacker News.
|
||||
s = ""
|
||||
}
|
||||
summaryCache[body] = s
|
||||
return s
|
||||
}
|
||||
|
||||
private let dateFormatter: DateFormatter = {
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .medium
|
||||
formatter.timeStyle = .none
|
||||
return formatter
|
||||
}()
|
||||
|
||||
private let timeFormatter: DateFormatter = {
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateStyle = .none
|
||||
formatter.timeStyle = .short
|
||||
return formatter
|
||||
}()
|
||||
|
||||
private var token: Int = 0
|
||||
|
||||
func timelineDateString(_ date: Date) -> String {
|
||||
|
||||
if NSCalendar.rs_dateIsToday(date) {
|
||||
return timeFormatter.string(from: date)
|
||||
}
|
||||
|
||||
return dateFormatter.string(from: date)
|
||||
}
|
||||
|
Loading…
Reference in New Issue