Handle undo of edited posts (#251)

This commit is contained in:
Angelo Stavrow 2023-07-23 07:19:52 -04:00 committed by GitHub
parent ba3f44b287
commit 73d219d0ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 68 additions and 12 deletions

View File

@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [Mac] Added a context-menu item to delete local posts from the post list.
- [Mac] Added methods to fetch device logs.
- [iOS, Mac] Added a way to search for text across all posts.
- [iOS, Mac] Added a way to refresh an edited post from the server copy.
### Changed
@ -25,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [Mac] Updated the URL and minimum version of the WriteFreely Swift package.
- [Mac] Upgraded the Sparkle package to v2.
- [Mac] The app now prompts you to reach out to our user forums if it detects a crash.
- [iOS, Mac] The app now reverts a post from edited to published status if you undo your changes.
### Fixed

View File

@ -244,6 +244,7 @@ extension WriteFreelyModel {
#if os(macOS)
self.selectedPost = cachedPost
#endif
cachedPost.status = PostStatus.published.rawValue
}
} catch {
self.currentError = AppError.genericError(error.localizedDescription)
@ -280,7 +281,6 @@ extension WriteFreelyModel {
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
}

View File

@ -12,6 +12,9 @@ struct PostEditorModel {
@AppStorage(WFDefaults.selectedCollectionURL, store: UserDefaults.shared) var selectedCollectionURL: URL?
@AppStorage(WFDefaults.lastDraftURL, store: UserDefaults.shared) var lastDraftURL: URL?
private(set) var initialPostTitle: String?
private(set) var initialPostBody: String?
#if os(macOS)
var postToUpdate: WFAPost?
#endif
@ -58,6 +61,21 @@ struct PostEditorModel {
return collection
}
/// Sets the initial values for title and body on a published post.
///
/// Used to detect if the title and body have changed back to their initial values. If the passed `WFAPost` isn't
/// published, any title and post values already stored are reset to `nil`.
/// - Parameter post: The `WFAPost` for which we're setting initial title/body values.
mutating func setInitialValues(for post: WFAPost) {
if post.status != PostStatus.published.rawValue {
initialPostTitle = nil
initialPostBody = nil
return
}
initialPostTitle = post.title
initialPostBody = post.body
}
private func fetchManagedObject(from objectURL: URL) -> NSManagedObject? {
let coordinator = LocalStorageManager.standard.container.persistentStoreCoordinator
guard let managedObjectID = coordinator.managedObjectID(forURIRepresentation: objectURL) else { return nil }

View File

@ -1063,7 +1063,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = "ActionExtension-iOS/ActionExtension-iOS.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 701;
CURRENT_PROJECT_VERSION = 702;
DEVELOPMENT_TEAM = TPPAB4YBA6;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "ActionExtension-iOS/Info.plist";
@ -1094,7 +1094,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
CODE_SIGN_ENTITLEMENTS = "ActionExtension-iOS/ActionExtension-iOS.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 701;
CURRENT_PROJECT_VERSION = 702;
DEVELOPMENT_TEAM = TPPAB4YBA6;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "ActionExtension-iOS/Info.plist";
@ -1237,7 +1237,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "WriteFreely-MultiPlatform (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 701;
CURRENT_PROJECT_VERSION = 702;
DEVELOPMENT_TEAM = TPPAB4YBA6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;
@ -1263,7 +1263,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "WriteFreely-MultiPlatform (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 701;
CURRENT_PROJECT_VERSION = 702;
DEVELOPMENT_TEAM = TPPAB4YBA6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;

View File

@ -84,7 +84,7 @@ struct PostEditorView: View {
})
.accessibilityHint(Text("Choose the blog you want to publish this post to"))
.disabled(post.body.count == 0)
} else {
} else if post.status == PostStatus.edited.rawValue {
Button(action: {
if model.account.isLoggedIn {
publishPost()
@ -95,6 +95,14 @@ struct PostEditorView: View {
Label("Publish", systemImage: "paperplane")
})
.disabled(post.status == PostStatus.published.rawValue || post.body.count == 0)
Button(action: {
model.updateFromServer(post: post)
}, label: {
Label("Revert", systemImage: "clock.arrow.circlepath")
})
.accessibilityHint(Text("Replace the edited post with the published version from the server"))
.disabled(post.status != PostStatus.edited.rawValue)
}
Button(action: {
sharePost()
@ -160,6 +168,7 @@ struct PostEditorView: View {
})
.onAppear(perform: {
self.selectedCollection = collections.first { $0.alias == post.collectionAlias }
model.editor.setInitialValues(for: post)
if post.status != PostStatus.published.rawValue {
DispatchQueue.main.async {
self.model.editor.saveLastDraft(post)
@ -201,9 +210,8 @@ struct PostEditorView: View {
LocalStorageManager.standard.saveContext()
model.publish(post: post)
}
#if os(iOS)
model.editor.setInitialValues(for: post)
self.hideKeyboard()
#endif
}
private func sharePost() {

View File

@ -2,6 +2,7 @@ import SwiftUI
struct PostTextEditingView: View {
@Environment(\.horizontalSizeClass) var horizontalSizeClass
@EnvironmentObject var model: WriteFreelyModel
@ObservedObject var post: WFAPost
@Binding var updatingTitleFromServer: Bool
@Binding var updatingBodyFromServer: Bool
@ -35,13 +36,16 @@ struct PostTextEditingView: View {
)
.accessibilityLabel(Text("Title (optional)"))
.accessibilityHint(Text("Add or edit the title for your post; use the Return key to skip to the body"))
.onChange(of: post.title) { _ in
.onChange(of: post.title) { value in
if post.status == PostStatus.published.rawValue && !updatingTitleFromServer {
post.status = PostStatus.edited.rawValue
}
if updatingTitleFromServer {
updatingTitleFromServer = false
}
if post.status == PostStatus.edited.rawValue && value == model.editor.initialPostTitle {
post.status = PostStatus.published.rawValue
}
}
MultilineTextField(
"Write...",
@ -51,13 +55,16 @@ struct PostTextEditingView: View {
)
.accessibilityLabel(Text("Body"))
.accessibilityHint(Text("Add or edit the body of your post"))
.onChange(of: post.body) { _ in
.onChange(of: post.body) { value in
if post.status == PostStatus.published.rawValue && !updatingBodyFromServer {
post.status = PostStatus.edited.rawValue
}
if updatingBodyFromServer {
updatingBodyFromServer = false
}
if post.status == PostStatus.edited.rawValue && value == model.editor.initialPostBody {
post.status = PostStatus.published.rawValue
}
}
}
.onChange(of: titleIsFirstResponder, perform: { value in

View File

@ -31,6 +31,17 @@ struct ActivePostToolbarView: View {
.frame(minWidth: 50, alignment: .center)
.layoutPriority(1)
.padding(.horizontal)
if activePost.status == PostStatus.edited.rawValue {
Button(action: {
model.editor.postToUpdate = activePost
model.updateFromServer(post: activePost)
model.selectedPost = nil
}, label: {
Image(systemName: "clock.arrow.circlepath")
.accessibilityLabel(Text("Revert post"))
.accessibilityHint(Text("Replace the edited post with the published version from the server"))
})
}
if activePost.status == PostStatus.local.rawValue {
Menu(content: {
Label("Publish To:", systemImage: "paperplane")
@ -131,6 +142,7 @@ struct ActivePostToolbarView: View {
LocalStorageManager.standard.saveContext()
model.publish(post: post)
}
model.editor.setInitialValues(for: post)
}
private func openSettingsWindow() {

View File

@ -16,6 +16,7 @@ struct PostEditorView: View {
.padding()
.background(Color(NSColor.controlBackgroundColor))
.onAppear(perform: {
model.editor.setInitialValues(for: post)
if post.status != PostStatus.published.rawValue {
DispatchQueue.main.async {
self.model.editor.saveLastDraft(post)

View File

@ -1,6 +1,7 @@
import SwiftUI
struct PostTextEditingView: View {
@EnvironmentObject var model: WriteFreelyModel
@ObservedObject var post: WFAPost
@Binding var updatingFromServer: Bool
@State private var appearance: PostAppearance = .serif
@ -74,9 +75,16 @@ struct PostTextEditingView: View {
private func onTextChange(_ text: String) {
extractTitle(text)
if post.status == PostStatus.published.rawValue && !updatingFromServer {
if !updatingFromServer {
if post.status == PostStatus.published.rawValue {
post.status = PostStatus.edited.rawValue
}
if post.status == PostStatus.edited.rawValue,
post.title == model.editor.initialPostTitle,
post.body == model.editor.initialPostBody {
post.status = PostStatus.published.rawValue
}
}
if updatingFromServer {
self.updatingFromServer = false