瀏覽代碼

Handle undo of edited posts (#251)

pull/253/head
Angelo Stavrow 9 月之前
committed by GitHub
父節點
當前提交
73d219d0ec
沒有發現已知的金鑰在資料庫的簽署中 GPG 金鑰 ID: 4AEE18F83AFDEB23
共有 9 個檔案被更改,包括 68 行新增12 行删除
  1. +2
    -0
      CHANGELOG.md
  2. +1
    -1
      Shared/Extensions/WriteFreelyModel+APIHandlers.swift
  3. +18
    -0
      Shared/PostEditor/PostEditorModel.swift
  4. +4
    -4
      WriteFreely-MultiPlatform.xcodeproj/project.pbxproj
  5. +11
    -3
      iOS/PostEditor/PostEditorView.swift
  6. +9
    -2
      iOS/PostEditor/PostTextEditingView.swift
  7. +12
    -0
      macOS/Navigation/ActivePostToolbarView.swift
  8. +1
    -0
      macOS/PostEditor/PostEditorView.swift
  9. +10
    -2
      macOS/PostEditor/PostTextEditingView.swift

+ 2
- 0
CHANGELOG.md 查看文件

@@ -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 a context-menu item to delete local posts from the post list.
- [Mac] Added methods to fetch device logs. - [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 search for text across all posts.
- [iOS, Mac] Added a way to refresh an edited post from the server copy.


### Changed ### 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] Updated the URL and minimum version of the WriteFreely Swift package.
- [Mac] Upgraded the Sparkle package to v2. - [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. - [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 ### Fixed




+ 1
- 1
Shared/Extensions/WriteFreelyModel+APIHandlers.swift 查看文件

@@ -244,6 +244,7 @@ extension WriteFreelyModel {
#if os(macOS) #if os(macOS)
self.selectedPost = cachedPost self.selectedPost = cachedPost
#endif #endif
cachedPost.status = PostStatus.published.rawValue
} }
} catch { } catch {
self.currentError = AppError.genericError(error.localizedDescription) self.currentError = AppError.genericError(error.localizedDescription)
@@ -280,7 +281,6 @@ extension WriteFreelyModel {
cachedPost.postId = fetchedPost.postId cachedPost.postId = fetchedPost.postId
cachedPost.rtl = fetchedPost.rtl ?? false cachedPost.rtl = fetchedPost.rtl ?? false
cachedPost.slug = fetchedPost.slug cachedPost.slug = fetchedPost.slug
cachedPost.status = PostStatus.published.rawValue
cachedPost.title = fetchedPost.title ?? "" cachedPost.title = fetchedPost.title ?? ""
cachedPost.updatedDate = fetchedPost.updatedDate cachedPost.updatedDate = fetchedPost.updatedDate
} }


+ 18
- 0
Shared/PostEditor/PostEditorModel.swift 查看文件

@@ -12,6 +12,9 @@ struct PostEditorModel {
@AppStorage(WFDefaults.selectedCollectionURL, store: UserDefaults.shared) var selectedCollectionURL: URL? @AppStorage(WFDefaults.selectedCollectionURL, store: UserDefaults.shared) var selectedCollectionURL: URL?
@AppStorage(WFDefaults.lastDraftURL, store: UserDefaults.shared) var lastDraftURL: URL? @AppStorage(WFDefaults.lastDraftURL, store: UserDefaults.shared) var lastDraftURL: URL?


private(set) var initialPostTitle: String?
private(set) var initialPostBody: String?

#if os(macOS) #if os(macOS)
var postToUpdate: WFAPost? var postToUpdate: WFAPost?
#endif #endif
@@ -58,6 +61,21 @@ struct PostEditorModel {
return collection 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? { private func fetchManagedObject(from objectURL: URL) -> NSManagedObject? {
let coordinator = LocalStorageManager.standard.container.persistentStoreCoordinator let coordinator = LocalStorageManager.standard.container.persistentStoreCoordinator
guard let managedObjectID = coordinator.managedObjectID(forURIRepresentation: objectURL) else { return nil } guard let managedObjectID = coordinator.managedObjectID(forURIRepresentation: objectURL) else { return nil }


+ 4
- 4
WriteFreely-MultiPlatform.xcodeproj/project.pbxproj 查看文件

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


+ 11
- 3
iOS/PostEditor/PostEditorView.swift 查看文件

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


private func sharePost() { private func sharePost() {


+ 9
- 2
iOS/PostEditor/PostTextEditingView.swift 查看文件

@@ -2,6 +2,7 @@ import SwiftUI


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


+ 12
- 0
macOS/Navigation/ActivePostToolbarView.swift 查看文件

@@ -31,6 +31,17 @@ struct ActivePostToolbarView: View {
.frame(minWidth: 50, alignment: .center) .frame(minWidth: 50, alignment: .center)
.layoutPriority(1) .layoutPriority(1)
.padding(.horizontal) .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 { if activePost.status == PostStatus.local.rawValue {
Menu(content: { Menu(content: {
Label("Publish To:", systemImage: "paperplane") Label("Publish To:", systemImage: "paperplane")
@@ -131,6 +142,7 @@ struct ActivePostToolbarView: View {
LocalStorageManager.standard.saveContext() LocalStorageManager.standard.saveContext()
model.publish(post: post) model.publish(post: post)
} }
model.editor.setInitialValues(for: post)
} }


private func openSettingsWindow() { private func openSettingsWindow() {


+ 1
- 0
macOS/PostEditor/PostEditorView.swift 查看文件

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


+ 10
- 2
macOS/PostEditor/PostTextEditingView.swift 查看文件

@@ -1,6 +1,7 @@
import SwiftUI import SwiftUI


struct PostTextEditingView: View { struct PostTextEditingView: View {
@EnvironmentObject var model: WriteFreelyModel
@ObservedObject var post: WFAPost @ObservedObject var post: WFAPost
@Binding var updatingFromServer: Bool @Binding var updatingFromServer: Bool
@State private var appearance: PostAppearance = .serif @State private var appearance: PostAppearance = .serif
@@ -74,8 +75,15 @@ struct PostTextEditingView: View {
private func onTextChange(_ text: String) { private func onTextChange(_ text: String) {
extractTitle(text) extractTitle(text)


if post.status == PostStatus.published.rawValue && !updatingFromServer {
post.status = PostStatus.edited.rawValue
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 { if updatingFromServer {


Loading…
取消
儲存