Page Menu
Home
Musing Studio
Search
Configure Global Search
Log In
Files
F10668612
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
62 KB
Subscribers
None
View Options
diff --git a/Shared/Models/LocalStorageModel.xcdatamodeld/LocalStorageModel.xcdatamodel/contents b/Shared/Models/LocalStorageModel.xcdatamodeld/LocalStorageModel.xcdatamodel/contents
index 92bd8c1..ff8b974 100644
--- a/Shared/Models/LocalStorageModel.xcdatamodeld/LocalStorageModel.xcdatamodel/contents
+++ b/Shared/Models/LocalStorageModel.xcdatamodeld/LocalStorageModel.xcdatamodel/contents
@@ -1,39 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17189" systemVersion="20A5364e" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="WFACollection" representedClassName="WFACollection" syncable="YES">
<attribute name="alias" optional="YES" attributeType="String"/>
<attribute name="blogDescription" optional="YES" attributeType="String"/>
<attribute name="email" optional="YES" attributeType="String"/>
<attribute name="isPublic" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="styleSheet" optional="YES" attributeType="String"/>
<attribute name="title" attributeType="String"/>
<attribute name="url" optional="YES" attributeType="String"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="alias"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<entity name="WFAPost" representedClassName="WFAPost" syncable="YES">
<attribute name="appearance" optional="YES" attributeType="String"/>
<attribute name="body" attributeType="String"/>
<attribute name="collectionAlias" optional="YES" attributeType="String"/>
<attribute name="createdDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+ <attribute name="hasNewerRemoteCopy" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="language" optional="YES" attributeType="String"/>
<attribute name="postId" optional="YES" attributeType="String"/>
<attribute name="rtl" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="slug" optional="YES" attributeType="String"/>
<attribute name="status" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="title" optional="YES" attributeType="String"/>
<attribute name="updatedDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<uniquenessConstraints>
<uniquenessConstraint>
<constraint value="postId"/>
</uniquenessConstraint>
</uniquenessConstraints>
</entity>
<elements>
<element name="WFACollection" positionX="14.806640625" positionY="202.9156341552734" width="128" height="148"/>
- <element name="WFAPost" positionX="287.377197265625" positionY="243.2452697753906" width="128" height="194"/>
+ <element name="WFAPost" positionX="287.377197265625" positionY="243.2452697753906" width="128" height="209"/>
</elements>
</model>
\ No newline at end of file
diff --git a/Shared/Models/WriteFreelyModel.swift b/Shared/Models/WriteFreelyModel.swift
index 1fdb457..baf1dd3 100644
--- a/Shared/Models/WriteFreelyModel.swift
+++ b/Shared/Models/WriteFreelyModel.swift
@@ -1,335 +1,360 @@
import Foundation
import WriteFreely
import Security
// MARK: - WriteFreelyModel
class WriteFreelyModel: ObservableObject {
@Published var account = AccountModel()
@Published var preferences = PreferencesModel()
@Published var store = PostListModel()
@Published var collections = CollectionListModel()
@Published var isLoggingIn: Bool = false
- @Published var selectedPost: Post?
+ @Published var selectedPost: WFAPost?
private var client: WFClient?
private let defaults = UserDefaults.standard
init() {
// Set the color scheme based on what's been saved in UserDefaults.
DispatchQueue.main.async {
self.preferences.appearance = self.defaults.integer(forKey: self.preferences.colorSchemeIntegerKey)
}
#if DEBUG
// for post in testPostData { store.add(post) }
#endif
DispatchQueue.main.async {
self.account.restoreState()
if self.account.isLoggedIn {
guard let serverURL = URL(string: self.account.server) else {
print("Server URL not found")
return
}
guard let token = self.fetchTokenFromKeychain(
username: self.account.username,
server: self.account.server
) else {
print("Could not fetch token from Keychain")
return
}
self.account.login(WFUser(token: token, username: self.account.username))
self.client = WFClient(for: serverURL)
self.client?.user = self.account.user
if self.collections.userCollections.count == 0 {
self.fetchUserCollections()
}
self.fetchUserPosts()
}
}
}
}
// MARK: - WriteFreelyModel API
extension WriteFreelyModel {
func login(to server: URL, as username: String, password: String) {
isLoggingIn = true
account.server = server.absoluteString
client = WFClient(for: server)
client?.login(username: username, password: password, completion: loginHandler)
}
func logout() {
guard let loggedInClient = client else {
do {
try purgeTokenFromKeychain(username: account.username, server: account.server)
account.logout()
} catch {
fatalError("Failed to log out persisted state")
}
return
}
loggedInClient.logout(completion: logoutHandler)
}
func fetchUserCollections() {
guard let loggedInClient = client else { return }
loggedInClient.getUserCollections(completion: fetchUserCollectionsHandler)
}
func fetchUserPosts() {
guard let loggedInClient = client else { return }
loggedInClient.getPosts(completion: fetchUserPostsHandler)
}
- func publish(post: Post) {
+ func publish(post: WFAPost) {
guard let loggedInClient = client else { return }
- if let existingPostId = post.wfPost.postId {
+ var wfPost = WFPost(
+ body: post.body ?? "",
+ title: post.title,
+ appearance: post.appearance,
+ language: post.language,
+ rtl: post.rtl,
+ createdDate: post.createdDate
+ )
+
+ if let existingPostId = post.postId {
// This is an existing post.
+ wfPost.postId = post.postId
+ wfPost.slug = post.slug
+ wfPost.updatedDate = post.updatedDate
+ wfPost.collectionAlias = post.collectionAlias
+
loggedInClient.updatePost(
postId: existingPostId,
- updatedPost: post.wfPost,
+ updatedPost: wfPost,
completion: publishHandler
)
} else {
// This is a new local draft.
loggedInClient.createPost(
- post: post.wfPost, in: post.collection?.alias, completion: publishHandler
+ post: wfPost, in: post.collectionAlias, completion: publishHandler
)
}
}
- func updateFromServer(post: Post) {
+ func updateFromServer(post: WFAPost) {
guard let loggedInClient = client else { return }
- guard let postId = post.wfPost.postId else { return }
+ guard let postId = post.postId else { return }
DispatchQueue.main.async {
self.selectedPost = post
}
loggedInClient.getPost(byId: postId, completion: updateFromServerHandler)
}
}
private extension WriteFreelyModel {
func loginHandler(result: Result<WFUser, Error>) {
DispatchQueue.main.async {
self.isLoggingIn = false
}
do {
let user = try result.get()
fetchUserCollections()
fetchUserPosts()
saveTokenToKeychain(user.token, username: user.username, server: account.server)
DispatchQueue.main.async {
self.account.login(user)
}
} catch WFError.notFound {
DispatchQueue.main.async {
self.account.currentError = AccountError.usernameNotFound
}
} catch WFError.unauthorized {
DispatchQueue.main.async {
self.account.currentError = AccountError.invalidPassword
}
} catch {
if (error as NSError).domain == NSURLErrorDomain,
(error as NSError).code == -1003 {
DispatchQueue.main.async {
self.account.currentError = AccountError.serverNotFound
}
}
}
}
func logoutHandler(result: Result<Bool, Error>) {
do {
_ = try result.get()
do {
try purgeTokenFromKeychain(username: account.user?.username, server: account.server)
client = nil
DispatchQueue.main.async {
self.account.logout()
self.collections.clearUserCollection()
self.store.purgeAllPosts()
}
} catch {
print("Something went wrong purging the token from the Keychain.")
}
} catch WFError.notFound {
// The user token is invalid or doesn't exist, so it's been invalidated by the server. Proceed with
// purging the token from the Keychain, destroying the client object, and setting the AccountModel to its
// logged-out state.
do {
try purgeTokenFromKeychain(username: account.user?.username, server: account.server)
client = nil
DispatchQueue.main.async {
self.collections.clearUserCollection()
self.account.logout()
self.store.purgeAllPosts()
}
} catch {
print("Something went wrong purging the token from the Keychain.")
}
} catch {
// We get a 'cannot parse response' (similar to what we were seeing in the Swift package) NSURLError here,
// so we're using a hacky workaround — if we get the NSURLError, but the AccountModel still thinks we're
// logged in, try calling the logout function again and see what we get.
// Conditional cast from 'Error' to 'NSError' always succeeds but is the only way to check error properties.
if (error as NSError).domain == NSURLErrorDomain,
(error as NSError).code == NSURLErrorCannotParseResponse {
if account.isLoggedIn {
self.logout()
}
}
}
}
func fetchUserCollectionsHandler(result: Result<[WFCollection], Error>) {
do {
let fetchedCollections = try result.get()
for fetchedCollection in fetchedCollections {
DispatchQueue.main.async {
let localCollection = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
localCollection.alias = fetchedCollection.alias
localCollection.blogDescription = fetchedCollection.description
localCollection.email = fetchedCollection.email
localCollection.isPublic = fetchedCollection.isPublic ?? false
localCollection.styleSheet = fetchedCollection.styleSheet
localCollection.title = fetchedCollection.title
localCollection.url = fetchedCollection.url
}
}
DispatchQueue.main.async {
PersistenceManager().saveContext()
}
} catch {
print(error)
}
}
func fetchUserPostsHandler(result: Result<[WFPost], Error>) {
do {
let fetchedPosts = try result.get()
- var fetchedPostsArray: [Post] = []
for fetchedPost in fetchedPosts {
- var post: Post
- if let matchingAlias = fetchedPost.collectionAlias {
- let matchingCachedCollection = (
- collections.userCollections.filter { $0.alias == matchingAlias }
- ).first
- post = Post(wfPost: fetchedPost, in: matchingCachedCollection)
- } else {
- post = Post(wfPost: fetchedPost)
- }
- fetchedPostsArray.append(post)
let managedPost = WFAPost(context: PersistenceManager.persistentContainer.viewContext)
managedPost.postId = fetchedPost.postId
managedPost.slug = fetchedPost.slug
managedPost.appearance = fetchedPost.appearance
managedPost.language = fetchedPost.language
managedPost.rtl = fetchedPost.rtl ?? false
managedPost.createdDate = fetchedPost.createdDate
managedPost.updatedDate = fetchedPost.updatedDate
managedPost.title = fetchedPost.title
managedPost.body = fetchedPost.body
managedPost.collectionAlias = fetchedPost.collectionAlias
- managedPost.status = PostStatus.published.rawValue // 0 = local, 1 = edited, 2 = published
+ managedPost.status = PostStatus.published.rawValue
}
DispatchQueue.main.async {
- self.store.updateStore(with: fetchedPostsArray)
PersistenceManager().saveContext()
}
} catch {
print(error)
}
}
func publishHandler(result: Result<WFPost, Error>) {
do {
- let wfPost = try result.get()
+ let fetchedPost = try result.get()
let foundPostIndex = store.posts.firstIndex(where: {
- $0.wfPost.title == wfPost.title && $0.wfPost.body == wfPost.body
+ $0.title == fetchedPost.title && $0.body == fetchedPost.body
})
guard let index = foundPostIndex else { return }
+ let cachedPost = self.store.posts[index]
+ cachedPost.appearance = fetchedPost.appearance
+ cachedPost.body = fetchedPost.body
+ cachedPost.collectionAlias = fetchedPost.collectionAlias
+ cachedPost.createdDate = fetchedPost.createdDate
+ cachedPost.language = fetchedPost.language
+ cachedPost.postId = fetchedPost.postId
+ cachedPost.rtl = fetchedPost.rtl ?? false
+ cachedPost.slug = fetchedPost.slug
+ cachedPost.status = PostStatus.published.rawValue
+ cachedPost.title = fetchedPost.title
+ cachedPost.updatedDate = fetchedPost.updatedDate
DispatchQueue.main.async {
- self.store.posts[index].wfPost = wfPost
+ PersistenceManager().saveContext()
}
} catch {
print(error)
}
}
func updateFromServerHandler(result: Result<WFPost, Error>) {
do {
let fetchedPost = try result.get()
+ guard let cachedPost = self.selectedPost else { return }
+ cachedPost.appearance = fetchedPost.appearance
+ cachedPost.body = fetchedPost.body
+ cachedPost.collectionAlias = fetchedPost.collectionAlias
+ cachedPost.createdDate = fetchedPost.createdDate
+ cachedPost.language = fetchedPost.language
+ cachedPost.postId = fetchedPost.postId
+ cachedPost.rtl = fetchedPost.rtl ?? false
+ cachedPost.slug = fetchedPost.slug
+ cachedPost.status = PostStatus.published.rawValue
+ cachedPost.title = fetchedPost.title
+ cachedPost.updatedDate = fetchedPost.updatedDate
DispatchQueue.main.async {
- guard let selectedPost = self.selectedPost else { return }
- self.store.replace(post: selectedPost, with: fetchedPost)
+ PersistenceManager().saveContext()
}
} catch {
print(error)
}
}
}
private extension WriteFreelyModel {
// MARK: - Keychain Helpers
func saveTokenToKeychain(_ token: String, username: String?, server: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecValueData as String: token.data(using: .utf8)!,
kSecAttrAccount as String: username ?? "anonymous",
kSecAttrService as String: server
]
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecDuplicateItem || status == errSecSuccess else {
fatalError("Error storing in Keychain with OSStatus: \(status)")
}
}
func purgeTokenFromKeychain(username: String?, server: String) throws {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: username ?? "anonymous",
kSecAttrService as String: server
]
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else {
fatalError("Error deleting from Keychain with OSStatus: \(status)")
}
}
func fetchTokenFromKeychain(username: String?, server: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: username ?? "anonymous",
kSecAttrService as String: server,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnAttributes as String: true,
kSecReturnData as String: true
]
var secItem: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &secItem)
guard status != errSecItemNotFound else {
return nil
}
guard status == errSecSuccess else {
fatalError("Error fetching from Keychain with OSStatus: \(status)")
}
guard let existingSecItem = secItem as? [String: Any],
let tokenData = existingSecItem[kSecValueData as String] as? Data,
let token = String(data: tokenData, encoding: .utf8) else {
return nil
}
return token
}
}
diff --git a/Shared/Navigation/ContentView.swift b/Shared/Navigation/ContentView.swift
index d95911c..88e9cf6 100644
--- a/Shared/Navigation/ContentView.swift
+++ b/Shared/Navigation/ContentView.swift
@@ -1,38 +1,38 @@
import SwiftUI
struct ContentView: View {
@EnvironmentObject var model: WriteFreelyModel
var body: some View {
NavigationView {
SidebarView()
PostListView(selectedCollection: nil, showAllPosts: true)
Text("Select a post, or create a new local draft.")
.foregroundColor(.secondary)
}
.environmentObject(model)
}
}
-struct ContentView_Previews: PreviewProvider {
- static var previews: some View {
- let userCollection1 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
- let userCollection2 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
- let userCollection3 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
- userCollection1.title = "Collection 1"
- userCollection2.title = "Collection 2"
- userCollection3.title = "Collection 3"
-
- let model = WriteFreelyModel()
- model.collections = CollectionListModel()
-
- for post in testPostData {
- model.store.add(post)
- }
- return ContentView()
- .environmentObject(model)
- .environment(\.managedObjectContext, PersistenceManager.persistentContainer.viewContext)
- }
-}
+//struct ContentView_Previews: PreviewProvider {
+// static var previews: some View {
+// let userCollection1 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+// let userCollection2 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+// let userCollection3 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+// userCollection1.title = "Collection 1"
+// userCollection2.title = "Collection 2"
+// userCollection3.title = "Collection 3"
+//
+// let model = WriteFreelyModel()
+// model.collections = CollectionListModel()
+//
+// for post in testPostData {
+// model.store.add(post)
+// }
+// return ContentView()
+// .environmentObject(model)
+// .environment(\.managedObjectContext, PersistenceManager.persistentContainer.viewContext)
+// }
+//}
diff --git a/Shared/PostEditor/PostEditorStatusToolbarView.swift b/Shared/PostEditor/PostEditorStatusToolbarView.swift
index 8c09962..85c5e65 100644
--- a/Shared/PostEditor/PostEditorStatusToolbarView.swift
+++ b/Shared/PostEditor/PostEditorStatusToolbarView.swift
@@ -1,136 +1,136 @@
import SwiftUI
struct PostEditorStatusToolbarView: View {
#if os(iOS)
@Environment(\.horizontalSizeClass) var horizontalSizeClass
#endif
@EnvironmentObject var model: WriteFreelyModel
- @ObservedObject var post: Post
+ @ObservedObject var post: WFAPost
var body: some View {
if post.hasNewerRemoteCopy {
#if os(iOS)
if horizontalSizeClass == .compact {
VStack {
PostStatusBadgeView(post: post)
HStack {
Text("⚠️ Newer copy on server. Replace local copy?")
.font(.caption)
.foregroundColor(.secondary)
Button(action: {
model.updateFromServer(post: post)
}, label: {
Image(systemName: "square.and.arrow.down")
})
}
.padding(.bottom)
}
.padding(.top)
} else {
HStack {
PostStatusBadgeView(post: post)
.padding(.trailing)
Text("⚠️ Newer copy on server. Replace local copy?")
.font(.callout)
.foregroundColor(.secondary)
Button(action: {
model.updateFromServer(post: post)
}, label: {
Image(systemName: "square.and.arrow.down")
})
}
}
#else
HStack {
PostStatusBadgeView(post: post)
.padding(.trailing)
Text("⚠️ Newer copy on server. Replace local copy?")
.font(.callout)
.foregroundColor(.secondary)
Button(action: {
model.updateFromServer(post: post)
}, label: {
Image(systemName: "square.and.arrow.down")
})
}
#endif
} else {
PostStatusBadgeView(post: post)
}
}
}
-#if DEBUG
-let testPost = Post(
- title: "Test Post Title",
- body: """
- Here's some cool sample body text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ultrices \
- posuere dignissim. Vestibulum a libero tempor, lacinia nulla vitae, congue purus. Nunc ac nulla quam. Duis \
- tincidunt eros augue, et volutpat tortor pulvinar ut. Nullam sit amet maximus urna. Phasellus non dignissim lacus.\
- Nulla ac posuere ex. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec \
- non molestie mauris. Suspendisse potenti. Vivamus at erat turpis.
-
- Pellentesque porttitor gravida tincidunt. Sed vitae eros non metus aliquam hendrerit. Aliquam sed risus suscipit \
- turpis dictum dictum. Duis lacus lectus, dictum vel felis in, rhoncus fringilla felis. Nunc id dolor nisl. Aliquam \
- euismod purus elit. Nullam egestas neque leo, sed aliquet ligula ultrices nec.
- """,
- createdDate: Date()
-)
-#endif
-
-struct ToolbarView_LocalPreviews: PreviewProvider {
- static var previews: some View {
- let model = WriteFreelyModel()
- let post = testPost
- return PostEditorStatusToolbarView(post: post)
- .environmentObject(model)
- }
-}
-
-struct ToolbarView_RemotePreviews: PreviewProvider {
- static var previews: some View {
- let model = WriteFreelyModel()
- let newerRemotePost = Post(
- title: testPost.wfPost.title ?? "",
- body: testPost.wfPost.body,
- createdDate: testPost.wfPost.createdDate ?? Date(),
- status: testPost.status,
- collection: testPost.collection
- )
- newerRemotePost.hasNewerRemoteCopy = true
- return PostEditorStatusToolbarView(post: newerRemotePost)
- .environmentObject(model)
- }
-}
-
-#if os(iOS)
-struct ToolbarView_CompactLocalPreviews: PreviewProvider {
- static var previews: some View {
- let model = WriteFreelyModel()
- let post = testPost
- return PostEditorStatusToolbarView(post: post)
- .environmentObject(model)
- .environment(\.horizontalSizeClass, .compact)
- }
-}
-#endif
-
-#if os(iOS)
-struct ToolbarView_CompactRemotePreviews: PreviewProvider {
- static var previews: some View {
- let model = WriteFreelyModel()
- let newerRemotePost = Post(
- title: testPost.wfPost.title ?? "",
- body: testPost.wfPost.body,
- createdDate: testPost.wfPost.createdDate ?? Date(),
- status: testPost.status,
- collection: testPost.collection
- )
- newerRemotePost.hasNewerRemoteCopy = true
- return PostEditorStatusToolbarView(post: newerRemotePost)
- .environmentObject(model)
- .environment(\.horizontalSizeClass, .compact)
- }
-}
-#endif
+//#if DEBUG
+//let testPost = Post(
+// title: "Test Post Title",
+// body: """
+// Here's some cool sample body text. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ultrices \
+// posuere dignissim. Vestibulum a libero tempor, lacinia nulla vitae, congue purus. Nunc ac nulla quam. Duis \
+// tincidunt eros augue, et volutpat tortor pulvinar ut. Nullam sit amet maximus urna. Phasellus non dignissim \
+// lacus. Nulla ac posuere ex. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus \
+// mus. Donec non molestie mauris. Suspendisse potenti. Vivamus at erat turpis.
+//
+// Pellentesque porttitor gravida tincidunt. Sed vitae eros non metus aliquam hendrerit. Aliquam sed risus suscipit \
+// turpis dictum dictum. Duis lacus lectus, dictum vel felis in, rhoncus fringilla felis. Nunc id dolor nisl. \
+// Aliquam euismod purus elit. Nullam egestas neque leo, sed aliquet ligula ultrices nec.
+// """,
+// createdDate: Date()
+//)
+//#endif
+//
+//struct ToolbarView_LocalPreviews: PreviewProvider {
+// static var previews: some View {
+// let model = WriteFreelyModel()
+// let post = testPost
+// return PostEditorStatusToolbarView(post: post)
+// .environmentObject(model)
+// }
+//}
+//
+//struct ToolbarView_RemotePreviews: PreviewProvider {
+// static var previews: some View {
+// let model = WriteFreelyModel()
+// let newerRemotePost = Post(
+// title: testPost.wfPost.title ?? "",
+// body: testPost.wfPost.body,
+// createdDate: testPost.wfPost.createdDate ?? Date(),
+// status: testPost.status,
+// collection: testPost.collection
+// )
+// newerRemotePost.hasNewerRemoteCopy = true
+// return PostEditorStatusToolbarView(post: newerRemotePost)
+// .environmentObject(model)
+// }
+//}
+//
+//#if os(iOS)
+//struct ToolbarView_CompactLocalPreviews: PreviewProvider {
+// static var previews: some View {
+// let model = WriteFreelyModel()
+// let post = testPost
+// return PostEditorStatusToolbarView(post: post)
+// .environmentObject(model)
+// .environment(\.horizontalSizeClass, .compact)
+// }
+//}
+//#endif
+//
+//#if os(iOS)
+//struct ToolbarView_CompactRemotePreviews: PreviewProvider {
+// static var previews: some View {
+// let model = WriteFreelyModel()
+// let newerRemotePost = Post(
+// title: testPost.wfPost.title ?? "",
+// body: testPost.wfPost.body,
+// createdDate: testPost.wfPost.createdDate ?? Date(),
+// status: testPost.status,
+// collection: testPost.collection
+// )
+// newerRemotePost.hasNewerRemoteCopy = true
+// return PostEditorStatusToolbarView(post: newerRemotePost)
+// .environmentObject(model)
+// .environment(\.horizontalSizeClass, .compact)
+// }
+//}
+//#endif
diff --git a/Shared/PostEditor/PostEditorView.swift b/Shared/PostEditor/PostEditorView.swift
index 6b9911b..9fcd55d 100644
--- a/Shared/PostEditor/PostEditorView.swift
+++ b/Shared/PostEditor/PostEditorView.swift
@@ -1,98 +1,87 @@
import SwiftUI
struct PostEditorView: View {
@EnvironmentObject var model: WriteFreelyModel
+ @Environment(\.managedObjectContext) var moc
- @ObservedObject var post: Post
+ @ObservedObject var post: WFAPost
+
+ @State private var postTitle = ""
+ @State private var postBody = ""
- @State private var isNewPost = false
- @State private var title = ""
var body: some View {
VStack {
- TextEditor(text: $title)
+ TextEditor(text: $postTitle)
.font(.title)
.frame(height: 100)
- .onChange(of: title) { _ in
- if post.status == .published && post.wfPost.title != title {
- post.status = .edited
+ .onChange(of: postTitle) { _ in
+ if post.status == PostStatus.published.rawValue && post.title != postTitle {
+ post.status = PostStatus.edited.rawValue
}
- post.wfPost.title = title
+ post.title = postTitle
}
- TextEditor(text: $post.wfPost.body)
+ TextEditor(text: $postBody)
.font(.body)
- .onChange(of: post.wfPost.body) { _ in
- if post.status == .published {
- post.status = .edited
+ .onChange(of: postBody) { _ in
+ if post.status == PostStatus.published.rawValue {
+ post.status = PostStatus.edited.rawValue
}
+ post.body = postBody
}
}
.padding()
.toolbar {
ToolbarItem(placement: .status) {
PostEditorStatusToolbarView(post: post)
}
ToolbarItem(placement: .primaryAction) {
Button(action: {
model.publish(post: post)
- post.status = .published
+ post.status = PostStatus.published.rawValue
}, label: {
Image(systemName: "paperplane")
})
}
}
.onAppear(perform: {
- title = post.wfPost.title ?? ""
- checkIfNewPost()
- if self.isNewPost {
- addNewPostToStore()
- }
+ postTitle = post.title ?? ""
+ postBody = post.body ?? ""
})
.onDisappear(perform: {
- if post.status == .edited {
+ if post.status == PostStatus.edited.rawValue {
DispatchQueue.main.async {
- model.store.update(post)
+ PersistenceManager().saveContext()
}
}
})
}
-
- private func checkIfNewPost() {
- self.isNewPost = !model.store.posts.contains(where: { $0.id == post.id })
- }
-
- private func addNewPostToStore() {
- withAnimation {
- model.store.add(post)
- self.isNewPost = false
- }
- }
}
-struct PostEditorView_NewLocalDraftPreviews: PreviewProvider {
- static var previews: some View {
- PostEditorView(post: Post())
- .environmentObject(WriteFreelyModel())
- }
-}
-
-struct PostEditorView_NewerLocalPostPreviews: PreviewProvider {
- static var previews: some View {
- return PostEditorView(post: testPost)
- .environmentObject(WriteFreelyModel())
- }
-}
-
-struct PostEditorView_NewerRemotePostPreviews: PreviewProvider {
- static var previews: some View {
- let newerRemotePost = Post(
- title: testPost.wfPost.title ?? "",
- body: testPost.wfPost.body,
- createdDate: testPost.wfPost.createdDate ?? Date(),
- status: testPost.status,
- collection: testPost.collection
- )
- newerRemotePost.hasNewerRemoteCopy = true
- return PostEditorView(post: newerRemotePost)
- .environmentObject(WriteFreelyModel())
- }
-}
+//struct PostEditorView_NewLocalDraftPreviews: PreviewProvider {
+// static var previews: some View {
+// PostEditorView(post: Post())
+// .environmentObject(WriteFreelyModel())
+// }
+//}
+//
+//struct PostEditorView_NewerLocalPostPreviews: PreviewProvider {
+// static var previews: some View {
+// return PostEditorView(post: testPost)
+// .environmentObject(WriteFreelyModel())
+// }
+//}
+//
+//struct PostEditorView_NewerRemotePostPreviews: PreviewProvider {
+// static var previews: some View {
+// let newerRemotePost = Post(
+// title: testPost.wfPost.title ?? "",
+// body: testPost.wfPost.body,
+// createdDate: testPost.wfPost.createdDate ?? Date(),
+// status: testPost.status,
+// collection: testPost.collection
+// )
+// newerRemotePost.hasNewerRemoteCopy = true
+// return PostEditorView(post: newerRemotePost)
+// .environmentObject(WriteFreelyModel())
+// }
+//}
diff --git a/Shared/PostList/PostCellView.swift b/Shared/PostList/PostCellView.swift
index 00122ff..535260e 100644
--- a/Shared/PostList/PostCellView.swift
+++ b/Shared/PostList/PostCellView.swift
@@ -1,40 +1,40 @@
import SwiftUI
struct PostCellView: View {
- @ObservedObject var post: Post
+ @ObservedObject var post: WFAPost
var body: some View {
HStack {
VStack(alignment: .leading) {
- Text(post.wfPost.title ?? "")
+ Text(post.title ?? "")
.font(.headline)
.lineLimit(1)
- Text(buildDateString(from: post.wfPost.createdDate ?? Date()))
+ Text(buildDateString(from: post.createdDate ?? Date()))
.font(.caption)
.lineLimit(1)
}
Spacer()
PostStatusBadgeView(post: post)
}
.padding(5)
}
func buildDateString(from date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .long
dateFormatter.timeStyle = .short
return dateFormatter.string(from: date)
}
}
-struct PostCell_Previews: PreviewProvider {
- static var previews: some View {
- let testPost = Post(
- title: "Test Post Title",
- body: "Here's some cool sample body text.",
- createdDate: Date()
- )
- return PostCellView(post: testPost)
- }
-}
+//struct PostCell_Previews: PreviewProvider {
+// static var previews: some View {
+// let testPost = Post(
+// title: "Test Post Title",
+// body: "Here's some cool sample body text.",
+// createdDate: Date()
+// )
+// return PostCellView(post: testPost)
+// }
+//}
diff --git a/Shared/PostList/PostListModel.swift b/Shared/PostList/PostListModel.swift
index 442c692..3f2d9c9 100644
--- a/Shared/PostList/PostListModel.swift
+++ b/Shared/PostList/PostListModel.swift
@@ -1,77 +1,91 @@
import Foundation
import WriteFreely
import CoreData
-struct PostListModel {
- var posts: [Post]
+class PostListModel: ObservableObject {
+ @Published var posts = [WFAPost]()
- init(posts: [Post] = []) {
- self.posts = posts
+ init() {
+ loadCachedPosts()
}
- mutating func add(_ post: Post) {
- posts.append(post)
+ func loadCachedPosts() {
+ let request = WFAPost.createFetchRequest()
+ let sort = NSSortDescriptor(key: "createdDate", ascending: false)
+ request.sortDescriptors = [sort]
+
+ posts = []
+ do {
+ let cachedPosts = try PersistenceManager.persistentContainer.viewContext.fetch(request)
+ posts.append(contentsOf: cachedPosts)
+ } catch {
+ print("Error: Failed to fetch cached posts.")
+ }
}
- mutating func purgeAllPosts() {
+ func purgeAllPosts() {
posts = []
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: "WFAPost")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try PersistenceManager.persistentContainer.persistentStoreCoordinator.execute(
deleteRequest, with: PersistenceManager.persistentContainer.viewContext
)
} catch {
print("Error: Failed to purge cached posts.")
}
}
- mutating func update(_ post: Post) {
- // Find the local copy in the store
- let localCopy = posts.first(where: { $0.id == post.id })
+// func add(_ post: WFAPost) {
+// posts.append(post)
+// }
- // If there's a local copy, update the updatedDate property of its WFPost
- if let localCopy = localCopy {
- localCopy.wfPost.updatedDate = Date()
- } else {
- print("Error: Local copy not found")
- }
- }
+// func update(_ post: WFAPost) {
+// // Find the local copy in the store
+// let localCopy = posts.first(where: { $0.id == post.id })
+//
+// // If there's a local copy, update the updatedDate property of its WFPost
+// if let localCopy = localCopy {
+// localCopy.wfPost.updatedDate = Date()
+// } else {
+// print("Error: Local copy not found")
+// }
+// }
- mutating func replace(post: Post, with fetchedPost: WFPost) {
- // Find the local copy in the store.
- let localCopy = posts.first(where: { $0.id == post.id })
+// func replace(post: Post, with fetchedPost: WFPost) {
+// // Find the local copy in the store.
+// let localCopy = posts.first(where: { $0.id == post.id })
+//
+// // Replace the local copy's wfPost property with the fetched copy.
+// if let localCopy = localCopy {
+// localCopy.wfPost = fetchedPost
+// DispatchQueue.main.async {
+// localCopy.hasNewerRemoteCopy = false
+// localCopy.status = .published
+// }
+// } else {
+// print("Error: Local copy not found")
+// }
+// }
- // Replace the local copy's wfPost property with the fetched copy.
- if let localCopy = localCopy {
- localCopy.wfPost = fetchedPost
- DispatchQueue.main.async {
- localCopy.hasNewerRemoteCopy = false
- localCopy.status = .published
- }
- } else {
- print("Error: Local copy not found")
- }
- }
-
- mutating func updateStore(with fetchedPosts: [Post]) {
- for fetchedPost in fetchedPosts {
- // Find the local copy in the store.
- let localCopy = posts.first(where: { $0.wfPost.postId == fetchedPost.wfPost.postId })
-
- // If there's a local copy, check which is newer; if not, add the fetched post to the store.
- if let localCopy = localCopy {
- // We do not discard the local copy; we simply set the hasNewerRemoteCopy flag accordingly.
- if let remoteCopyUpdatedDate = fetchedPost.wfPost.updatedDate,
- let localCopyUpdatedDate = localCopy.wfPost.updatedDate {
- localCopy.hasNewerRemoteCopy = remoteCopyUpdatedDate > localCopyUpdatedDate
- } else {
- print("Error: could not determine which copy of post is newer")
- }
- } else {
- add(fetchedPost)
- }
- }
- }
+// func updateStore(with fetchedPosts: [Post]) {
+// for fetchedPost in fetchedPosts {
+// // Find the local copy in the store.
+// let localCopy = posts.first(where: { $0.wfPost.postId == fetchedPost.wfPost.postId })
+//
+// // If there's a local copy, check which is newer; if not, add the fetched post to the store.
+// if let localCopy = localCopy {
+// // We do not discard the local copy; we simply set the hasNewerRemoteCopy flag accordingly.
+// if let remoteCopyUpdatedDate = fetchedPost.wfPost.updatedDate,
+// let localCopyUpdatedDate = localCopy.wfPost.updatedDate {
+// localCopy.hasNewerRemoteCopy = remoteCopyUpdatedDate > localCopyUpdatedDate
+// } else {
+// print("Error: could not determine which copy of post is newer")
+// }
+// } else {
+// add(fetchedPost)
+// }
+// }
+// }
}
diff --git a/Shared/PostList/PostListView.swift b/Shared/PostList/PostListView.swift
index 48c5500..81d23b6 100644
--- a/Shared/PostList/PostListView.swift
+++ b/Shared/PostList/PostListView.swift
@@ -1,209 +1,206 @@
import SwiftUI
struct PostListView: View {
@EnvironmentObject var model: WriteFreelyModel
+ @Environment(\.managedObjectContext) var moc
+
+ @FetchRequest(
+ entity: WFAPost.entity(),
+ sortDescriptors: [NSSortDescriptor(keyPath: \WFAPost.createdDate, ascending: true)]
+ ) var posts: FetchedResults<WFAPost>
+
@State var selectedCollection: WFACollection?
@State var showAllPosts: Bool = false
#if os(iOS)
@State private var isPresentingSettings = false
#endif
var body: some View {
#if os(iOS)
GeometryReader { geometry in
List {
ForEach(showPosts(for: selectedCollection)) { post in
- NavigationLink(
- destination: PostEditorView(post: post)
- ) {
- PostCellView(
- post: post
- )
+ NavigationLink(destination: PostEditorView(post: post)) {
+ PostCellView(post: post)
}
}
}
.environmentObject(model)
.navigationTitle(
showAllPosts ? "All Posts" : selectedCollection?.title ?? (
model.account.server == "https://write.as" ? "Anonymous" : "Drafts"
)
)
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: {
createNewLocalDraft()
}, label: {
Image(systemName: "square.and.pencil")
})
}
ToolbarItem(placement: .bottomBar) {
HStack {
Button(action: {
isPresentingSettings = true
}, label: {
Image(systemName: "gear")
}).sheet(
isPresented: $isPresentingSettings,
onDismiss: {
isPresentingSettings = false
},
content: {
SettingsView(isPresented: $isPresentingSettings)
}
)
.padding(.leading)
Spacer()
Text(pluralizedPostCount(for: showPosts(for: selectedCollection)))
.foregroundColor(.secondary)
Spacer()
Button(action: {
reloadFromServer()
}, label: {
Image(systemName: "arrow.clockwise")
})
.disabled(!model.account.isLoggedIn)
}
.padding()
.frame(width: geometry.size.width)
}
}
}
#else //if os(macOS)
List {
ForEach(showPosts(for: selectedCollection)) { post in
- NavigationLink(
- destination: PostEditorView(post: post)
- ) {
- PostCellView(
- post: post
- )
+ NavigationLink(destination: PostEditorView(post: post)) {
+ PostCellView(post: post)
}
}
}
.navigationTitle(
showAllPosts ? "All Posts" : selectedCollection?.title ?? (
model.account.server == "https://write.as" ? "Anonymous" : "Drafts"
)
)
.navigationSubtitle(pluralizedPostCount(for: showPosts(for: selectedCollection)))
.toolbar {
Button(action: {
createNewLocalDraft()
}, label: {
Image(systemName: "square.and.pencil")
})
Button(action: {
reloadFromServer()
}, label: {
Image(systemName: "arrow.clockwise")
})
.disabled(!model.account.isLoggedIn)
}
#endif
}
- private func pluralizedPostCount(for posts: [Post]) -> String {
+ private func pluralizedPostCount(for posts: [WFAPost]) -> String {
if posts.count == 1 {
return "1 post"
} else {
return "\(posts.count) posts"
}
}
- private func showPosts(for collection: WFACollection?) -> [Post] {
+ private func showPosts(for collection: WFACollection?) -> [WFAPost] {
if showAllPosts {
return model.store.posts
} else {
- var posts: [Post]
if let selectedCollection = collection {
- posts = model.store.posts.filter { $0.wfPost.collectionAlias == selectedCollection.alias }
+ return model.store.posts.filter { $0.collectionAlias == selectedCollection.alias }
} else {
- posts = model.store.posts.filter { $0.wfPost.collectionAlias == nil }
+ return model.store.posts.filter { $0.collectionAlias == nil }
}
- return posts
}
}
private func reloadFromServer() {
DispatchQueue.main.async {
model.collections.clearUserCollection()
model.fetchUserCollections()
model.fetchUserPosts()
}
}
private func createNewLocalDraft() {
let post = Post()
let managedPost = WFAPost(context: PersistenceManager.persistentContainer.viewContext)
managedPost.createdDate = post.wfPost.createdDate
managedPost.title = post.wfPost.title
managedPost.body = post.wfPost.body
managedPost.status = PostStatus.local.rawValue
DispatchQueue.main.async {
- model.store.add(post)
+// model.store.add(post)
PersistenceManager().saveContext()
}
}
}
-struct PostList_Previews: PreviewProvider {
- static var previews: some View {
- let userCollection1 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
- let userCollection2 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
- let userCollection3 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
-
- userCollection1.title = "Collection 1"
- userCollection2.title = "Collection 2"
- userCollection3.title = "Collection 3"
-
- let testPostData = [
- Post(
- title: "My First Post",
- body: "Look at me, creating a first post! That's cool.",
- createdDate: Date(timeIntervalSince1970: 1595429452),
- status: .published,
- collection: userCollection1
- ),
- Post(
- title: "Post 2: The Quickening",
- body: "See, here's the rule about Highlander jokes: _there can be only one_.",
- createdDate: Date(timeIntervalSince1970: 1595514125),
- status: .edited,
- collection: userCollection1
- ),
- Post(
- title: "The Post Revolutions",
- body: "I can never keep the Matrix movie order straight. Why not just call them part 2 and part 3?",
- createdDate: Date(timeIntervalSince1970: 1595600006)
- ),
- Post(
- title: "Episode IV: A New Post",
- body: "How many movies does this person watch? How many movie-title jokes will they make?",
- createdDate: Date(timeIntervalSince1970: 1596219877),
- status: .published,
- collection: userCollection2
- ),
- Post(
- title: "Fast (Post) Five",
- body: "Look, it was either a Fast and the Furious reference, or a Resident Evil reference."
- ),
- Post(
- title: "Post: The Final Chapter",
- body: "And there you have it, a Resident Evil movie reference.",
- createdDate: Date(timeIntervalSince1970: 1596043684),
- status: .edited,
- collection: userCollection3
- )
- ]
-
- let model = WriteFreelyModel()
- for post in testPostData {
- model.store.add(post)
- }
- return Group {
- PostListView(selectedCollection: userCollection1)
- .environmentObject(model)
- }
- }
-}
+//struct PostList_Previews: PreviewProvider {
+// static var previews: some View {
+// let userCollection1 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+// let userCollection2 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+// let userCollection3 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+//
+// userCollection1.title = "Collection 1"
+// userCollection2.title = "Collection 2"
+// userCollection3.title = "Collection 3"
+//
+// let testPostData = [
+// Post(
+// title: "My First Post",
+// body: "Look at me, creating a first post! That's cool.",
+// createdDate: Date(timeIntervalSince1970: 1595429452),
+// status: .published,
+// collection: userCollection1
+// ),
+// Post(
+// title: "Post 2: The Quickening",
+// body: "See, here's the rule about Highlander jokes: _there can be only one_.",
+// createdDate: Date(timeIntervalSince1970: 1595514125),
+// status: .edited,
+// collection: userCollection1
+// ),
+// Post(
+// title: "The Post Revolutions",
+// body: "I can never keep the Matrix movie order straight. Why not just call them part 2 and part 3?",
+// createdDate: Date(timeIntervalSince1970: 1595600006)
+// ),
+// Post(
+// title: "Episode IV: A New Post",
+// body: "How many movies does this person watch? How many movie-title jokes will they make?",
+// createdDate: Date(timeIntervalSince1970: 1596219877),
+// status: .published,
+// collection: userCollection2
+// ),
+// Post(
+// title: "Fast (Post) Five",
+// body: "Look, it was either a Fast and the Furious reference, or a Resident Evil reference."
+// ),
+// Post(
+// title: "Post: The Final Chapter",
+// body: "And there you have it, a Resident Evil movie reference.",
+// createdDate: Date(timeIntervalSince1970: 1596043684),
+// status: .edited,
+// collection: userCollection3
+// )
+// ]
+//
+// let model = WriteFreelyModel()
+// for post in testPostData {
+// model.store.add(post)
+// }
+// return Group {
+// PostListView(selectedCollection: userCollection1)
+// .environmentObject(model)
+// }
+// }
+//}
diff --git a/Shared/PostList/PostStatusBadgeView.swift b/Shared/PostList/PostStatusBadgeView.swift
index d7f0ac9..6681ced 100644
--- a/Shared/PostList/PostStatusBadgeView.swift
+++ b/Shared/PostList/PostStatusBadgeView.swift
@@ -1,103 +1,103 @@
import SwiftUI
struct PostStatusBadgeView: View {
- @ObservedObject var post: Post
+ @ObservedObject var post: WFAPost
var body: some View {
- let (badgeLabel, badgeColor) = setupBadgeProperties(for: post.status)
+ let (badgeLabel, badgeColor) = setupBadgeProperties(for: PostStatus(rawValue: post.status)!)
Text(badgeLabel)
.font(.caption)
.fontWeight(.semibold)
.foregroundColor(.white)
.textCase(.uppercase)
.lineLimit(1)
.padding(EdgeInsets(top: 2.5, leading: 7.5, bottom: 2.5, trailing: 7.5))
.background(badgeColor)
.clipShape(RoundedRectangle(cornerRadius: 5.0, style: .circular))
}
func setupBadgeProperties(for status: PostStatus) -> (String, Color) {
var badgeLabel: String
var badgeColor: Color
switch status {
case .local:
badgeLabel = "local"
badgeColor = Color(red: 0.75, green: 0.5, blue: 0.85, opacity: 1.0)
case .edited:
badgeLabel = "edited"
badgeColor = Color(red: 0.75, green: 0.7, blue: 0.1, opacity: 1.0)
case .published:
badgeLabel = "published"
badgeColor = .gray
}
return (badgeLabel, badgeColor)
}
}
-#if DEBUG
-let userCollection1 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
-let userCollection2 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
-let userCollection3 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
-
-let testPostData = [
- Post(
- title: "My First Post",
- body: "Look at me, creating a first post! That's cool.",
- createdDate: Date(timeIntervalSince1970: 1595429452),
- status: .published,
- collection: userCollection1
- ),
- Post(
- title: "Post 2: The Quickening",
- body: "See, here's the rule about Highlander jokes: _there can be only one_.",
- createdDate: Date(timeIntervalSince1970: 1595514125),
- status: .edited,
- collection: userCollection1
- ),
- Post(
- title: "The Post Revolutions",
- body: "I can never keep the Matrix movie order straight. Why not just call them part 2 and part 3?",
- createdDate: Date(timeIntervalSince1970: 1595600006)
- ),
- Post(
- title: "Episode IV: A New Post",
- body: "How many movies does this person watch? How many movie-title jokes will they make?",
- createdDate: Date(timeIntervalSince1970: 1596219877),
- status: .published,
- collection: userCollection2
- ),
- Post(
- title: "Fast (Post) Five",
- body: "Look, it was either a Fast and the Furious reference, or a Resident Evil reference."
- ),
- Post(
- title: "Post: The Final Chapter",
- body: "And there you have it, a Resident Evil movie reference.",
- createdDate: Date(timeIntervalSince1970: 1596043684),
- status: .edited,
- collection: userCollection3
- )
-]
-#endif
-
-struct PostStatusBadge_LocalDraftPreviews: PreviewProvider {
- static var previews: some View {
- userCollection1.title = "Collection 1"
- return PostStatusBadgeView(post: testPostData[2])
- }
-}
-
-struct PostStatusBadge_EditedPreviews: PreviewProvider {
- static var previews: some View {
- userCollection1.title = "Collection 1"
- return PostStatusBadgeView(post: testPostData[1])
- }
-}
-
-struct PostStatusBadge_PublishedPreviews: PreviewProvider {
- static var previews: some View {
- PostStatusBadgeView(post: testPostData[0])
- }
-}
+//#if DEBUG
+//let userCollection1 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+//let userCollection2 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+//let userCollection3 = WFACollection(context: PersistenceManager.persistentContainer.viewContext)
+//
+//let testPostData = [
+// Post(
+// title: "My First Post",
+// body: "Look at me, creating a first post! That's cool.",
+// createdDate: Date(timeIntervalSince1970: 1595429452),
+// status: .published,
+// collection: userCollection1
+// ),
+// Post(
+// title: "Post 2: The Quickening",
+// body: "See, here's the rule about Highlander jokes: _there can be only one_.",
+// createdDate: Date(timeIntervalSince1970: 1595514125),
+// status: .edited,
+// collection: userCollection1
+// ),
+// Post(
+// title: "The Post Revolutions",
+// body: "I can never keep the Matrix movie order straight. Why not just call them part 2 and part 3?",
+// createdDate: Date(timeIntervalSince1970: 1595600006)
+// ),
+// Post(
+// title: "Episode IV: A New Post",
+// body: "How many movies does this person watch? How many movie-title jokes will they make?",
+// createdDate: Date(timeIntervalSince1970: 1596219877),
+// status: .published,
+// collection: userCollection2
+// ),
+// Post(
+// title: "Fast (Post) Five",
+// body: "Look, it was either a Fast and the Furious reference, or a Resident Evil reference."
+// ),
+// Post(
+// title: "Post: The Final Chapter",
+// body: "And there you have it, a Resident Evil movie reference.",
+// createdDate: Date(timeIntervalSince1970: 1596043684),
+// status: .edited,
+// collection: userCollection3
+// )
+//]
+//#endif
+//
+//struct PostStatusBadge_LocalDraftPreviews: PreviewProvider {
+// static var previews: some View {
+// userCollection1.title = "Collection 1"
+// return PostStatusBadgeView(post: testPostData[2])
+// }
+//}
+//
+//struct PostStatusBadge_EditedPreviews: PreviewProvider {
+// static var previews: some View {
+// userCollection1.title = "Collection 1"
+// return PostStatusBadgeView(post: testPostData[1])
+// }
+//}
+//
+//struct PostStatusBadge_PublishedPreviews: PreviewProvider {
+// static var previews: some View {
+// PostStatusBadgeView(post: testPostData[0])
+// }
+//}
diff --git a/WFAPost+CoreDataProperties.swift b/WFAPost+CoreDataProperties.swift
index ba31720..d7ef424 100644
--- a/WFAPost+CoreDataProperties.swift
+++ b/WFAPost+CoreDataProperties.swift
@@ -1,26 +1,35 @@
+//
+// WFAPost+CoreDataProperties.swift
+// WriteFreely-MultiPlatform
+//
+// Created by Angelo Stavrow on 2020-09-08.
+//
+//
+
import Foundation
import CoreData
extension WFAPost {
@nonobjc public class func createFetchRequest() -> NSFetchRequest<WFAPost> {
return NSFetchRequest<WFAPost>(entityName: "WFAPost")
}
@NSManaged public var appearance: String?
@NSManaged public var body: String?
@NSManaged public var collectionAlias: String?
@NSManaged public var createdDate: Date?
@NSManaged public var language: String?
@NSManaged public var postId: String?
@NSManaged public var rtl: Bool
@NSManaged public var slug: String?
@NSManaged public var status: Int32
@NSManaged public var title: String?
@NSManaged public var updatedDate: Date?
+ @NSManaged public var hasNewerRemoteCopy: Bool
}
extension WFAPost: Identifiable {
}
diff --git a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist
index 2723ebe..6cd8075 100644
--- a/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/WriteFreely-MultiPlatform.xcodeproj/xcuserdata/angelo.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
- <integer>0</integer>
+ <integer>1</integer>
</dict>
<key>WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
- <integer>1</integer>
+ <integer>0</integer>
</dict>
</dict>
</dict>
</plist>
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, May 15, 5:02 AM (6 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3239475
Attached To
rWFSUI WriteFreely SwiftUI
Event Timeline
Log In to Comment