Merge pull request #167 from writeas/fix-post-status-bug

Fix post-status bug on publishing changes
This commit is contained in:
Angelo Stavrow 2021-02-01 10:12:27 -05:00 committed by GitHub
commit 849c093748
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 141 additions and 120 deletions

View File

@ -37,6 +37,7 @@ class WriteFreelyModel: ObservableObject {
private let defaults = UserDefaults.standard private let defaults = UserDefaults.standard
private let monitor = NWPathMonitor() private let monitor = NWPathMonitor()
private let queue = DispatchQueue(label: "NetworkMonitor") private let queue = DispatchQueue(label: "NetworkMonitor")
private var postToUpdate: WFAPost?
init() { init() {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -145,6 +146,8 @@ extension WriteFreelyModel {
} }
func publish(post: WFAPost) { func publish(post: WFAPost) {
postToUpdate = nil
if !hasNetworkConnection { if !hasNetworkConnection {
DispatchQueue.main.async { self.isPresentingNetworkErrorAlert = true } DispatchQueue.main.async { self.isPresentingNetworkErrorAlert = true }
return return
@ -173,10 +176,8 @@ extension WriteFreelyModel {
if let existingPostId = post.postId { if let existingPostId = post.postId {
// This is an existing post. // This is an existing post.
postToUpdate = post
wfPost.postId = post.postId wfPost.postId = post.postId
wfPost.slug = post.slug
wfPost.updatedDate = post.updatedDate
wfPost.collectionAlias = post.collectionAlias
loggedInClient.updatePost( loggedInClient.updatePost(
postId: existingPostId, postId: existingPostId,
@ -407,37 +408,55 @@ private extension WriteFreelyModel {
// See: https://github.com/writeas/writefreely-swift/issues/20 // See: https://github.com/writeas/writefreely-swift/issues/20
do { do {
let fetchedPost = try result.get() let fetchedPost = try result.get()
let request = WFAPost.createFetchRequest() // If this is an updated post, check it against postToUpdate.
let matchBodyPredicate = NSPredicate(format: "body == %@", fetchedPost.body) if let updatingPost = self.postToUpdate {
if let fetchedPostTitle = fetchedPost.title { updatingPost.appearance = fetchedPost.appearance
let matchTitlePredicate = NSPredicate(format: "title == %@", fetchedPostTitle) updatingPost.body = fetchedPost.body
request.predicate = NSCompoundPredicate( updatingPost.createdDate = fetchedPost.createdDate
andPredicateWithSubpredicates: [ updatingPost.language = fetchedPost.language
matchTitlePredicate, updatingPost.postId = fetchedPost.postId
matchBodyPredicate updatingPost.rtl = fetchedPost.rtl ?? false
] updatingPost.slug = fetchedPost.slug
) updatingPost.status = PostStatus.published.rawValue
} else { updatingPost.title = fetchedPost.title ?? ""
request.predicate = matchBodyPredicate updatingPost.updatedDate = fetchedPost.updatedDate
}
do {
let cachedPostsResults = try LocalStorageManager.persistentContainer.viewContext.fetch(request)
guard let cachedPost = cachedPostsResults.first else { return }
cachedPost.appearance = fetchedPost.appearance
cachedPost.body = fetchedPost.body
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 { DispatchQueue.main.async {
LocalStorageManager().saveContext() LocalStorageManager().saveContext()
} }
} catch { } else {
print("Error: Failed to fetch cached posts") // Otherwise if it's a newly-published post, find it in the local store.
let request = WFAPost.createFetchRequest()
let matchBodyPredicate = NSPredicate(format: "body == %@", fetchedPost.body)
if let fetchedPostTitle = fetchedPost.title {
let matchTitlePredicate = NSPredicate(format: "title == %@", fetchedPostTitle)
request.predicate = NSCompoundPredicate(
andPredicateWithSubpredicates: [
matchTitlePredicate,
matchBodyPredicate
]
)
} else {
request.predicate = matchBodyPredicate
}
do {
let cachedPostsResults = try LocalStorageManager.persistentContainer.viewContext.fetch(request)
guard let cachedPost = cachedPostsResults.first else { return }
cachedPost.appearance = fetchedPost.appearance
cachedPost.body = fetchedPost.body
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 {
LocalStorageManager().saveContext()
}
} catch {
print("Error: Failed to fetch cached posts")
}
} }
} catch { } catch {
print(error) print(error)

View File

@ -112,19 +112,21 @@ struct PostListView: View {
) )
.toolbar { .toolbar {
ToolbarItemGroup(placement: .primaryAction) { ToolbarItemGroup(placement: .primaryAction) {
ActivePostToolbarView() if model.selectedPost != nil {
.alert(isPresented: $model.isPresentingNetworkErrorAlert, content: { ActivePostToolbarView(activePost: model.selectedPost!)
Alert( .alert(isPresented: $model.isPresentingNetworkErrorAlert, content: {
title: Text("Connection Error"), Alert(
message: Text(""" title: Text("Connection Error"),
message: Text("""
There is no internet connection at the moment. \ There is no internet connection at the moment. \
Please reconnect or try again later. Please reconnect or try again later.
"""), """),
dismissButton: .default(Text("OK"), action: { dismissButton: .default(Text("OK"), action: {
model.isPresentingNetworkErrorAlert = false model.isPresentingNetworkErrorAlert = false
}) })
) )
}) })
}
} }
} }
.navigationTitle( .navigationTitle(

View File

@ -2,6 +2,7 @@ import SwiftUI
struct ActivePostToolbarView: View { struct ActivePostToolbarView: View {
@EnvironmentObject var model: WriteFreelyModel @EnvironmentObject var model: WriteFreelyModel
@ObservedObject var activePost: WFAPost
@State private var isPresentingSharingServicePicker: Bool = false @State private var isPresentingSharingServicePicker: Bool = false
@State private var selectedCollection: WFACollection? @State private var selectedCollection: WFACollection?
@ -11,100 +12,96 @@ struct ActivePostToolbarView: View {
) var collections: FetchedResults<WFACollection> ) var collections: FetchedResults<WFACollection>
var body: some View { var body: some View {
if let activePost = model.selectedPost { HStack {
HStack { if model.account.isLoggedIn &&
if model.account.isLoggedIn && activePost.status != PostStatus.local.rawValue &&
activePost.status != PostStatus.local.rawValue && !(activePost.wasDeletedFromServer || activePost.hasNewerRemoteCopy) {
!(activePost.wasDeletedFromServer || activePost.hasNewerRemoteCopy) { Section(header: Text("Move To:")) {
Section(header: Text("Move To:")) { Picker(selection: $selectedCollection, label: Text("Move To…"), content: {
Picker(selection: $selectedCollection, label: Text("Move To…"), content: { Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")")
Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")") .tag(nil as WFACollection?)
.tag(nil as WFACollection?)
Divider()
ForEach(collections) { collection in
Text("\(collection.title)").tag(collection as WFACollection?)
}
})
}
}
PostEditorStatusToolbarView(post: activePost)
.frame(minWidth: 50, alignment: .center)
.layoutPriority(1)
.padding(.horizontal)
if activePost.status == PostStatus.local.rawValue {
Menu(content: {
Label("Publish To:", systemImage: "paperplane")
Divider() Divider()
ForEach(collections) { collection in
Text("\(collection.title)").tag(collection as WFACollection?)
}
})
}
}
PostEditorStatusToolbarView(post: activePost)
.frame(minWidth: 50, alignment: .center)
.layoutPriority(1)
.padding(.horizontal)
if activePost.status == PostStatus.local.rawValue {
Menu(content: {
Label("Publish To:", systemImage: "paperplane")
Divider()
Button(action: {
if model.account.isLoggedIn {
withAnimation {
activePost.collectionAlias = nil
publishPost(activePost)
}
} else {
openSettingsWindow()
}
}, label: {
Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")")
})
ForEach(collections) { collection in
Button(action: { Button(action: {
if model.account.isLoggedIn { if model.account.isLoggedIn {
withAnimation { withAnimation {
activePost.collectionAlias = nil activePost.collectionAlias = collection.alias
publishPost(activePost) publishPost(activePost)
} }
} else { } else {
openSettingsWindow() openSettingsWindow()
} }
}, label: { }, label: {
Text("\(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")") Text("\(collection.title)")
}) })
ForEach(collections) { collection in
Button(action: {
if model.account.isLoggedIn {
withAnimation {
activePost.collectionAlias = collection.alias
publishPost(activePost)
}
} else {
openSettingsWindow()
}
}, label: {
Text("\(collection.title)")
})
}
}, label: {
Label("Publish…", systemImage: "paperplane")
})
.disabled(activePost.body.isEmpty)
.help("Publish the post to the web.\(model.account.isLoggedIn ? "" : " You must be logged in to do this.")") // swiftlint:disable:this line_length
} else {
HStack(spacing: 4) {
Button(
action: {
self.isPresentingSharingServicePicker = true
},
label: { Image(systemName: "square.and.arrow.up") }
)
.disabled(activePost.status == PostStatus.local.rawValue)
.help("Copy the post's URL to your Mac's pasteboard.")
.popover(isPresented: $isPresentingSharingServicePicker) {
PostEditorSharingPicker(
isPresented: $isPresentingSharingServicePicker,
sharingItems: createPostUrl()
)
.frame(width: .zero, height: .zero)
}
Button(action: { publishPost(activePost) }, label: { Image(systemName: "paperplane") })
.disabled(activePost.body.isEmpty || activePost.status == PostStatus.published.rawValue)
.help("Publish the post to the web.\(model.account.isLoggedIn ? "" : " You must be logged in to do this.")") // swiftlint:disable:this line_length
} }
}, label: {
Label("Publish…", systemImage: "paperplane")
})
.disabled(model.selectedPost?.body.isEmpty ?? true)
.help("Publish the post to the web.\(model.account.isLoggedIn ? "" : " You must be logged in to do this.")") // swiftlint:disable:this line_length
} else {
HStack(spacing: 4) {
Button(
action: {
self.isPresentingSharingServicePicker = true
},
label: { Image(systemName: "square.and.arrow.up") }
)
.disabled(activePost.status == PostStatus.local.rawValue)
.help("Copy the post's URL to your Mac's pasteboard.")
.popover(isPresented: $isPresentingSharingServicePicker) {
PostEditorSharingPicker(
isPresented: $isPresentingSharingServicePicker,
sharingItems: createPostUrl()
)
.frame(width: .zero, height: .zero)
}
Button(action: { publishPost(activePost) }, label: { Image(systemName: "paperplane") })
.disabled(activePost.body.isEmpty || activePost.status == PostStatus.published.rawValue)
.help("Publish the post to the web.\(model.account.isLoggedIn ? "" : " You must be logged in to do this.")") // swiftlint:disable:this line_length
} }
} }
.onAppear(perform: {
self.selectedCollection = collections.first { $0.alias == activePost.collectionAlias }
})
.onChange(of: selectedCollection, perform: { [selectedCollection] newCollection in
if activePost.collectionAlias == newCollection?.alias {
return
} else {
withAnimation {
activePost.collectionAlias = newCollection?.alias
model.move(post: activePost, from: selectedCollection, to: newCollection)
}
}
})
} else {
EmptyView()
} }
.onAppear(perform: {
self.selectedCollection = collections.first { $0.alias == activePost.collectionAlias }
})
.onChange(of: selectedCollection, perform: { [selectedCollection] newCollection in
if activePost.collectionAlias == newCollection?.alias {
return
} else {
withAnimation {
activePost.collectionAlias = newCollection?.alias
model.move(post: activePost, from: selectedCollection, to: newCollection)
}
}
})
} }
private func createPostUrl() -> [Any] { private func createPostUrl() -> [Any] {
@ -117,6 +114,9 @@ struct ActivePostToolbarView: View {
} }
private func publishPost(_ post: WFAPost) { private func publishPost(_ post: WFAPost) {
if post != model.selectedPost {
return
}
DispatchQueue.main.async { DispatchQueue.main.async {
LocalStorageManager().saveContext() LocalStorageManager().saveContext()
model.publish(post: post) model.publish(post: post)