2018-12-02 19:51:32 +01:00
|
|
|
//
|
2019-10-20 09:28:00 +02:00
|
|
|
// ArticleStringFormatter.swift
|
2018-12-02 19:51:32 +01:00
|
|
|
// NetNewsWire
|
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 8/31/15.
|
|
|
|
// Copyright © 2015 Ranchero Software, LLC. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import Articles
|
|
|
|
import RSParser
|
|
|
|
|
2019-10-20 09:28:00 +02:00
|
|
|
struct ArticleStringFormatter {
|
2018-12-02 19:51:32 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-04-07 23:05:13 +02:00
|
|
|
static func truncatedTitle(_ article: Article, forHTML: Bool = false) -> String {
|
|
|
|
guard let title = article.sanitizedTitle(forHTML: forHTML) else {
|
2018-12-02 19:51:32 +01:00
|
|
|
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: "")
|
2020-04-07 23:05:13 +02:00
|
|
|
|
|
|
|
if !forHTML {
|
|
|
|
s = s.rsparser_stringByDecodingHTMLEntities()
|
|
|
|
}
|
|
|
|
|
2020-01-17 03:09:18 +01:00
|
|
|
s = s.trimmingWhitespace
|
|
|
|
s = s.collapsingWhitespace
|
2018-12-02 19:51:32 +01:00
|
|
|
|
|
|
|
let maxLength = 1000
|
|
|
|
if s.count < maxLength {
|
|
|
|
titleCache[title] = s
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
s = (s as NSString).substring(to: maxLength)
|
|
|
|
titleCache[title] = s
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2020-04-07 23:05:13 +02:00
|
|
|
static func attributedTruncatedTitle(_ article: Article) -> NSAttributedString {
|
|
|
|
let title = truncatedTitle(article, forHTML: true)
|
2020-04-10 23:02:49 +02:00
|
|
|
let attributed = NSAttributedString(html: title)
|
2020-04-07 23:05:13 +02:00
|
|
|
return attributed
|
|
|
|
}
|
|
|
|
|
2018-12-02 19:51:32 +01:00
|
|
|
static func truncatedSummary(_ article: Article) -> String {
|
|
|
|
guard let body = article.body else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2019-05-14 07:16:44 +02:00
|
|
|
let key = article.articleID + article.accountID
|
|
|
|
if let cachedBody = summaryCache[key] {
|
2018-12-02 19:51:32 +01:00
|
|
|
return cachedBody
|
|
|
|
}
|
|
|
|
var s = body.rsparser_stringByDecodingHTMLEntities()
|
2020-01-17 03:09:18 +01:00
|
|
|
s = s.strippingHTML(maxCharacters: 250)
|
|
|
|
s = s.trimmingWhitespace
|
|
|
|
s = s.collapsingWhitespace
|
2018-12-02 19:51:32 +01:00
|
|
|
if s == "Comments" { // Hacker News.
|
|
|
|
s = ""
|
|
|
|
}
|
2019-05-14 07:16:44 +02:00
|
|
|
summaryCache[key] = s
|
2018-12-02 19:51:32 +01:00
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
|
|
|
static func dateString(_ date: Date) -> String {
|
2020-01-12 00:19:32 +01:00
|
|
|
if Calendar.dateIsToday(date) {
|
2018-12-02 19:51:32 +01:00
|
|
|
return timeFormatter.string(from: date)
|
|
|
|
}
|
|
|
|
return dateFormatter.string(from: date)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|