Page MenuHomeMusing Studio

No OneTemporary

diff --git a/Shared/PostList/PostCellView.swift b/Shared/PostList/PostCellView.swift
index 14fa2fc..1522141 100644
--- a/Shared/PostList/PostCellView.swift
+++ b/Shared/PostList/PostCellView.swift
@@ -1,64 +1,86 @@
import SwiftUI
struct PostCellView: View {
+ @EnvironmentObject var model: WriteFreelyModel
@ObservedObject var post: WFAPost
var collectionName: String?
static let createdDateFormat: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale.current
formatter.dateStyle = .long
formatter.timeStyle = .short
return formatter
}()
+ var titleText: String {
+ if post.title.isEmpty {
+ return model.posts.getBodyPreview(of: post)
+ }
+ return post.title
+ }
+
var body: some View {
HStack {
VStack(alignment: .leading) {
if let collectionName = collectionName {
Text(collectionName)
.font(.caption)
.foregroundColor(.secondary)
.padding(EdgeInsets(top: 3, leading: 4, bottom: 3, trailing: 4))
.overlay(RoundedRectangle(cornerRadius: 2).stroke(Color.secondary, lineWidth: 1))
}
- Text(post.title)
+ Text(titleText)
.font(.headline)
Text(post.createdDate ?? Date(), formatter: Self.createdDateFormat)
.font(.caption)
.foregroundColor(.secondary)
.padding(.top, -3)
}
Spacer()
PostStatusBadgeView(post: post)
}
.padding(5)
}
}
struct PostCell_AllPostsPreviews: PreviewProvider {
static var previews: some View {
let context = LocalStorageManager.persistentContainer.viewContext
let testPost = WFAPost(context: context)
testPost.title = "Test Post Title"
testPost.body = "Here's some cool sample body text."
testPost.createdDate = Date()
return PostCellView(post: testPost, collectionName: "My Cool Blog")
.environment(\.managedObjectContext, context)
}
}
struct PostCell_NormalPreviews: PreviewProvider {
static var previews: some View {
let context = LocalStorageManager.persistentContainer.viewContext
let testPost = WFAPost(context: context)
testPost.title = "Test Post Title"
testPost.body = "Here's some cool sample body text."
testPost.collectionAlias = "My Cool Blog"
testPost.createdDate = Date()
return PostCellView(post: testPost)
.environment(\.managedObjectContext, context)
}
}
+
+struct PostCell_NoTitlePreviews: PreviewProvider {
+ static var previews: some View {
+ let context = LocalStorageManager.persistentContainer.viewContext
+ let testPost = WFAPost(context: context)
+ testPost.title = ""
+ testPost.body = "Here's some cool sample body text."
+ testPost.collectionAlias = "My Cool Blog"
+ testPost.createdDate = Date()
+
+ return PostCellView(post: testPost)
+ .environment(\.managedObjectContext, context)
+ }
+}
diff --git a/Shared/PostList/PostListModel.swift b/Shared/PostList/PostListModel.swift
index e6464e4..98e158b 100644
--- a/Shared/PostList/PostListModel.swift
+++ b/Shared/PostList/PostListModel.swift
@@ -1,21 +1,124 @@
import SwiftUI
import CoreData
class PostListModel: ObservableObject {
func remove(_ post: WFAPost) {
LocalStorageManager.persistentContainer.viewContext.delete(post)
LocalStorageManager().saveContext()
}
func purgePublishedPosts() {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "WFAPost")
fetchRequest.predicate = NSPredicate(format: "status != %i", 0)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try LocalStorageManager.persistentContainer.viewContext.executeAndMergeChanges(using: deleteRequest)
} catch {
print("Error: Failed to purge cached posts.")
}
}
+
+ func getBodyPreview(of post: WFAPost) -> String {
+ var elidedPostBody: String = ""
+
+ // Strip any markdown from the post body.
+ let strippedPostBody = stripMarkdown(from: post.body)
+
+ // Extract lede from post.
+ elidedPostBody = extractLede(from: strippedPostBody)
+
+ return elidedPostBody
+ }
+}
+
+private extension PostListModel {
+
+ func stripMarkdown(from string: String) -> String {
+ var strippedString = string
+ strippedString = stripHeadingOctothorpes(from: strippedString)
+ strippedString = stripImages(from: strippedString, keepAltText: true)
+ return strippedString
+ }
+
+ func stripHeadingOctothorpes(from string: String) -> String {
+ let newLines = CharacterSet.newlines
+ var processedComponents: [String] = []
+ let components = string.components(separatedBy: newLines)
+ for component in components {
+ if component.isEmpty {
+ continue
+ }
+ var newString = component
+ while newString.first == "#" {
+ newString.removeFirst()
+ }
+ if newString.hasPrefix(" ") {
+ newString.removeFirst()
+ }
+ processedComponents.append(newString)
+ }
+ let headinglessString = processedComponents.joined(separator: "\n\n")
+ return headinglessString
+ }
+
+ func stripImages(from string: String, keepAltText: Bool = false) -> String {
+ let pattern = #"!\[[\"]?(.*?)[\"|]?\]\(.*?\)"#
+ var processedComponents: [String] = []
+ let components = string.components(separatedBy: .newlines)
+ for component in components {
+ if component.isEmpty { continue }
+ var processedString: String = component
+ if keepAltText {
+ let regex = try? NSRegularExpression(pattern: pattern, options: [])
+ if let matches = regex?.matches(
+ in: component, options: [], range: NSRange(location: 0, length: component.utf16.count)
+ ) {
+ for match in matches {
+ if let range = Range(match.range(at: 1), in: component) {
+ processedString = "\(component[range])"
+ }
+ }
+ }
+ } else {
+ let range = component.startIndex..<component.endIndex
+ processedString = component.replacingOccurrences(
+ of: pattern,
+ with: "",
+ options: .regularExpression,
+ range: range
+ )
+ }
+ if processedString.isEmpty { continue }
+ processedComponents.append(processedString)
+ }
+ return processedComponents.joined(separator: "\n\n")
+ }
+
+ func extractLede(from string: String) -> String {
+ let truncatedString = string.prefix(80)
+ let terminatingPunctuation = ".。?"
+ let terminatingCharacters = CharacterSet(charactersIn: terminatingPunctuation).union(.newlines)
+
+ var lede: String = ""
+ let sentences = truncatedString.components(separatedBy: terminatingCharacters)
+ if let firstSentence = (sentences.filter { !$0.isEmpty }).first {
+ if truncatedString.count > firstSentence.count {
+ if terminatingPunctuation.contains(truncatedString[firstSentence.endIndex]) {
+ lede = String(truncatedString[...firstSentence.endIndex])
+ } else {
+ lede = firstSentence
+ }
+ } else if truncatedString.count == firstSentence.count {
+ if string.count > 80 {
+ if let endOfStringIndex = truncatedString.lastIndex(of: " ") {
+ lede = truncatedString[..<endOfStringIndex] + "…"
+ }
+ } else {
+ lede = firstSentence
+ }
+ }
+ }
+ return lede
+ }
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jan 31, 4:44 PM (18 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3145907

Event Timeline