Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore autosave revision dialog #12947

Merged
merged 34 commits into from
Dec 2, 2019
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ab8d533
Display a status label for autosaved posts
guarani Nov 10, 2019
11b1996
Load post autosave content if user so chooses
guarani Nov 12, 2019
7a2613e
Removed copyrights from new file
guarani Nov 14, 2019
0277ac3
Removed trailing whitespace violation
guarani Nov 14, 2019
c092bb0
Merge branch 'develop' into issue/12141-restore-autosave
guarani Nov 17, 2019
ce207c3
Create edit revision before loading Gutenberg
guarani Nov 21, 2019
26156cf
Support loading autosave content into the editor
guarani Nov 21, 2019
e4fb4fa
Merge branch 'develop' into issue/12141-restore-autosave
guarani Nov 21, 2019
c0fae7a
Adhered to better code conventions
guarani Nov 22, 2019
0c3148e
Ignore autosaves on posts with local changes
guarani Nov 22, 2019
750a19f
Fix bug where autosave was ignored
guarani Nov 22, 2019
76a04b0
Clarify localized string comments
guarani Nov 22, 2019
70e2da7
Tighten access control
guarani Nov 22, 2019
7903a99
Update release notes for load autosave feature
guarani Nov 24, 2019
c067c91
Make autosave status string name more descriptive
guarani Nov 24, 2019
71cc8cc
Join autosave localized copy into one string
guarani Nov 24, 2019
1fded19
Clarified autosave copy comment for translators
guarani Nov 25, 2019
599b62c
Improved post.hasAutosaveRevision to read better
guarani Nov 25, 2019
9c7146f
Simplified autosave dialog closures
guarani Nov 25, 2019
6dc9028
Renamed post.loadAutosaveRevision for clarity
guarani Nov 25, 2019
a1e0ca5
Removed trailing whitespace from line
guarani Nov 26, 2019
a5f9d65
Merge branch 'develop' into issue/12141-restore-autosave
guarani Nov 29, 2019
dd76748
Include post autosave identifier in core data
guarani Nov 29, 2019
cce2701
Use identifier to determine autosave presence
guarani Nov 29, 2019
e8a3107
Abstract edit post logic into separate file
guarani Nov 29, 2019
c5d09ae
Define loadAutosaveRevision in editor initializer.
guarani Nov 29, 2019
717654d
Remove dialog from UIAlertController namespace
guarani Nov 29, 2019
b373cf0
Posts with local changes do not show autosave
guarani Nov 29, 2019
3a1dc36
Autosave status test uses sync remote status
guarani Nov 29, 2019
785316e
Remove autosaveIdentifier default value of zero
guarani Dec 2, 2019
a1a034d
Make PostListEditorPresenter struct not enum
guarani Dec 2, 2019
e5d6648
Merge branch 'develop' into issue/12141-restore-autosave
guarani Dec 2, 2019
47c72ae
Merge branch 'develop' into issue/12141-restore-autosave
guarani Dec 2, 2019
ff57ba6
Remove unintended change from release notes
guarani Dec 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions MIGRATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
This file documents changes in the data model. Please explain any changes to the
data model as well as any custom migrations.


## WordPress 94

@guarani 2019-11-28

- `AbstractPost` added `autosaveIdentifier` (`nullable` `NSNumber`) property.

## WordPress 93

@guarani 2019-10-27
Expand Down
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
13.8
-----
* When a post has an autosave, the autosave version can be loaded into the editor.
* Block editor: Images from Image Block can now be previewed full screen by tapping on them.
* Support: Fix issue that caused 'Message failed to send' error.
* WebView: Fix iOS 13 crash with popover.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,12 @@ extension AbstractPost: ImageSourceInformation {
var isLocalDraft: Bool {
return self.isDraft() && !self.hasRemote()
}

/// An autosave revision may include post title, content and/or excerpt.
var hasAutosaveRevision: Bool {
guard let autosaveRevisionIdentifier = autosaveIdentifier?.intValue else {
return false
}
return autosaveRevisionIdentifier > 0
}
}
1 change: 1 addition & 0 deletions WordPress/Classes/Models/AbstractPost.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ typedef NS_ENUM(NSUInteger, AbstractPostRemoteStatus) {
@property (nonatomic, copy, nullable) NSString *autosaveExcerpt;
@property (nonatomic, copy, nullable) NSString *autosaveTitle;
@property (nonatomic, copy, nullable) NSDate *autosaveModifiedDate;
@property (nonatomic, copy, nullable) NSNumber *autosaveIdentifier;

// Revision management
- (AbstractPost *)createRevision;
Expand Down
1 change: 1 addition & 0 deletions WordPress/Classes/Models/AbstractPost.m
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ @implementation AbstractPost
@dynamic autosaveExcerpt;
@dynamic autosaveTitle;
@dynamic autosaveModifiedDate;
@dynamic autosaveIdentifier;

@synthesize restorableStatus;

Expand Down
1 change: 1 addition & 0 deletions WordPress/Classes/Services/PostService.m
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ - (void)updatePost:(AbstractPost *)post withRemotePost:(RemotePost *)remotePost
post.autosaveExcerpt = remotePost.autosave.excerpt;
post.autosaveContent = remotePost.autosave.content;
post.autosaveModifiedDate = remotePost.autosave.modifiedDate;
post.autosaveIdentifier = remotePost.autosave.identifier;

if ([post isKindOfClass:[Page class]]) {
Page *pagePost = (Page *)post;
Expand Down
10 changes: 5 additions & 5 deletions WordPress/Classes/Utility/Editor/EditorFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ class EditorFactory {

// MARK: - Editor: Instantiation

func instantiateEditor(for post: AbstractPost, replaceEditor: @escaping ReplaceEditorBlock) -> EditorViewController {
func instantiateEditor(for post: AbstractPost, loadAutosaveRevision: Bool = false, replaceEditor: @escaping ReplaceEditorBlock) -> EditorViewController {
if gutenbergSettings.mustUseGutenberg(for: post) {
return createGutenbergVC(with: post, replaceEditor: replaceEditor)
return createGutenbergVC(with: post, loadAutosaveRevision: loadAutosaveRevision, replaceEditor: replaceEditor)
} else {
return AztecPostViewController(post: post, replaceEditor: replaceEditor)
return AztecPostViewController(post: post, loadAutosaveRevision: loadAutosaveRevision, replaceEditor: replaceEditor)
}
}

private func createGutenbergVC(with post: AbstractPost, replaceEditor: @escaping ReplaceEditorBlock) -> GutenbergViewController {
let gutenbergVC = GutenbergViewController(post: post, replaceEditor: replaceEditor)
private func createGutenbergVC(with post: AbstractPost, loadAutosaveRevision: Bool, replaceEditor: @escaping ReplaceEditorBlock) -> GutenbergViewController {
let gutenbergVC = GutenbergViewController(post: post, loadAutosaveRevision: loadAutosaveRevision, replaceEditor: replaceEditor)

if gutenbergSettings.shouldAutoenableGutenberg(for: post) {
gutenbergSettings.setGutenbergEnabled(true, for: post.blog, source: .onBlockPostOpening)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ class AztecPostViewController: UIViewController, PostEditor {
}
}

/// If true, apply autosave content when the editor creates a revision.
///
private let loadAutosaveRevision: Bool

/// Active Downloads
///
fileprivate var activeMediaRequests = [ImageDownloader.Task]()
Expand Down Expand Up @@ -434,20 +438,16 @@ class AztecPostViewController: UIViewController, PostEditor {

// MARK: - Initializers

/// Initializer
///
/// - Parameters:
/// - post: the post to edit in this VC. Must be already assigned to a `ManagedObjectContext`
/// since that's necessary for the edits to be saved.
///
required init(
post: AbstractPost,
loadAutosaveRevision: Bool = false,
replaceEditor: @escaping (EditorViewController, EditorViewController) -> (),
editorSession: PostEditorAnalyticsSession? = nil) {

precondition(post.managedObjectContext != nil)

self.post = post
self.loadAutosaveRevision = loadAutosaveRevision
self.replaceEditor = replaceEditor
self.editorSession = editorSession ?? PostEditorAnalyticsSession(editor: .classic, post: post)

Expand Down Expand Up @@ -485,7 +485,7 @@ class AztecPostViewController: UIViewController, PostEditor {
WPFontManager.loadNotoFontFamily()

registerAttachmentImageProviders()
createRevisionOfPost()
createRevisionOfPost(loadAutosaveRevision: loadAutosaveRevision)

// Setup
configureNavigationBar()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ class GutenbergViewController: UIViewController, PostEditor {
}
}

/// If true, apply autosave content when the editor creates a revision.
///
private let loadAutosaveRevision: Bool

let navigationBarManager = PostEditorNavigationBarManager()

lazy var attachmentDelegate = AztecAttachmentDelegate(post: post)
Expand Down Expand Up @@ -218,10 +222,12 @@ class GutenbergViewController: UIViewController, PostEditor {
// MARK: - Initializers
required init(
post: AbstractPost,
loadAutosaveRevision: Bool = false,
replaceEditor: @escaping (EditorViewController, EditorViewController) -> (),
editorSession: PostEditorAnalyticsSession? = nil) {

self.post = post
self.loadAutosaveRevision = loadAutosaveRevision

self.replaceEditor = replaceEditor
verificationPromptHelper = AztecVerificationPromptHelper(account: self.post.blog.account)
Expand Down Expand Up @@ -249,8 +255,8 @@ class GutenbergViewController: UIViewController, PostEditor {

override func viewDidLoad() {
super.viewDidLoad()
createRevisionOfPost(loadAutosaveRevision: loadAutosaveRevision)
setupGutenbergView()
shiki marked this conversation as resolved.
Show resolved Hide resolved
createRevisionOfPost()
configureNavigationBar()
refreshInterface()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class EditPostViewController: UIViewController {
@objc var openWithPostPost: Bool = false
/// appear with media pre-inserted into the post
var insertedMedia: [Media]? = nil
private let loadAutosaveRevision: Bool

@objc fileprivate(set) var post: Post?
fileprivate var hasShownEditor = false
Expand Down Expand Up @@ -42,8 +43,8 @@ class EditPostViewController: UIViewController {
/// Initialize as an editor with the provided post
///
/// - Parameter post: post to edit
@objc convenience init(post: Post) {
self.init(post: post, blog: post.blog)
@objc convenience init(post: Post, loadAutosaveRevision: Bool = false) {
self.init(post: post, blog: post.blog, loadAutosaveRevision: loadAutosaveRevision)
}


Expand All @@ -60,8 +61,9 @@ class EditPostViewController: UIViewController {
/// - post: the post to edit
/// - blog: the blog to create a post for, if post is nil
/// - Note: it's preferable to use one of the convenience initializers
fileprivate init(post: Post?, blog: Blog) {
fileprivate init(post: Post?, blog: Blog, loadAutosaveRevision: Bool = false) {
self.post = post
self.loadAutosaveRevision = loadAutosaveRevision
if let post = post {
if !post.originalIsDraft() {
editingExistingPost = true
Expand Down Expand Up @@ -125,6 +127,7 @@ class EditPostViewController: UIViewController {
fileprivate func showEditor() {
let editor = editorFactory.instantiateEditor(
for: postToEdit(),
loadAutosaveRevision: loadAutosaveRevision,
replaceEditor: { [weak self] (editor, replacement) in
self?.replaceEditor(editor: editor, replacement: replacement)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class PostCardStatusViewModel: NSObject {
return generateFailedStatusMessage()
} else if post.remoteStatus == .pushing {
return NSLocalizedString("Uploading post...", comment: "Message displayed on a post's card when the post has failed to upload")
} else if !post.hasLocalChanges() && post.hasAutosaveRevision {
return StatusMessages.hasUnsavedChanges
} else {
return post.statusForDisplay()
}
Expand Down Expand Up @@ -98,6 +100,10 @@ class PostCardStatusViewModel: NSObject {
return (autoUploadAction == .upload || post.wasAutoUploadCancelled) ? .warning : .error
}

if post.hasAutosaveRevision {
return .warning(.shade40)
}

switch status {
case .pending:
return .success
Expand Down Expand Up @@ -262,5 +268,7 @@ class PostCardStatusViewModel: NSObject {
comment: "Message displayed on a post's card when the post has failed to upload")
static let localChanges = NSLocalizedString("Local changes",
comment: "A status label for a post that only exists on the user's iOS device, and has not yet been published to their blog.")
static let hasUnsavedChanges = NSLocalizedString("You've made unsaved changes to this post",
comment: "Message displayed on a post's card when the post has unsaved changes")
}
}
9 changes: 8 additions & 1 deletion WordPress/Classes/ViewRelated/Post/PostEditor+Publish.swift
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ extension PostEditor where Self: UIViewController {
}

// TODO: Rip this out and put it into the PostService
func createRevisionOfPost() {
func createRevisionOfPost(loadAutosaveRevision: Bool = false) {

if post.isLocalRevision, post.original?.postTitle == nil, post.original?.content == nil {
// Editing a locally made revision has bit of weirdness in how autosave and
Expand Down Expand Up @@ -440,6 +440,13 @@ extension PostEditor where Self: UIViewController {

managedObjectContext.performAndWait {
post = self.post.createRevision()

if loadAutosaveRevision {
post.postTitle = post.autosaveTitle
post.mt_excerpt = post.autosaveExcerpt
post.content = post.autosaveContent
}

ContextManager.sharedInstance().save(managedObjectContext)
}
}
Expand Down
14 changes: 14 additions & 0 deletions WordPress/Classes/ViewRelated/Post/PostEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ protocol PostEditor: class, UIViewControllerTransitioningDelegate {
///
var isOpenedDirectlyForPhotoPost: Bool { get set }

/// Initializer
///
/// - Parameters:
/// - post: the post to edit. Must be already assigned to a `ManagedObjectContext` since
/// that's necessary for the edits to be saved.
/// - loadAutosaveRevision: if true, apply autosave content when the editor creates a revision.
/// - replaceEditor: a closure that handles switching from one editor to another
/// - editorSession: post editor analytics session
init(
post: AbstractPost,
loadAutosaveRevision: Bool,
replaceEditor: @escaping (EditorViewController, EditorViewController) -> (),
editorSession: PostEditorAnalyticsSession?)

/// Media items to be inserted on the post after creation
///
/// - Parameter media: the media items to add
Expand Down
69 changes: 69 additions & 0 deletions WordPress/Classes/ViewRelated/Post/PostListEditorPresenter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import Foundation

/// Handle a user tapping a post in the post list. If an autosave revision is available, give the
/// user the option through a dialog alert to load the autosave (or just load the regular post) into
/// the editor.
/// Analytics are also tracked.
enum PostListEditorPresenter {
guarani marked this conversation as resolved.
Show resolved Hide resolved

static func handle(post: Post, in postListViewController: PostListViewController) {

// Autosaves are ignored for posts with local changes.
if !post.hasLocalChanges(), post.hasAutosaveRevision, let saveDate = post.dateModified, let autosaveDate = post.autosaveModifiedDate {
let autosaveViewController = autosaveOptionsViewController(forSaveDate: saveDate, autosaveDate: autosaveDate, didTapOption: { loadAutosaveRevision in
openEditor(with: post, loadAutosaveRevision: loadAutosaveRevision, in: postListViewController)
})
postListViewController.present(autosaveViewController, animated: true)
} else {
openEditor(with: post, loadAutosaveRevision: false, in: postListViewController)
}
}

private static func openEditor(with post: Post, loadAutosaveRevision: Bool, in postListViewController: PostListViewController) {
let editor = EditPostViewController(post: post, loadAutosaveRevision: loadAutosaveRevision)
editor.modalPresentationStyle = .fullScreen
postListViewController.present(editor, animated: false)
WPAppAnalytics.track(.postListEditAction, withProperties: postListViewController.propertiesForAnalytics(), with: post)
}

private static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}()

private static let timeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
return formatter
}()

private static func dateAndTime(for date: Date) -> String {
return dateFormatter.string(from: date) + " @ " + timeFormatter.string(from: date)
}

/// A dialog giving the user the choice between loading the current version a post or its autosaved version.
private static func autosaveOptionsViewController(forSaveDate saveDate: Date, autosaveDate: Date, didTapOption: @escaping (_ loadAutosaveRevision: Bool) -> Void) -> UIAlertController {

let title = NSLocalizedString("Which version would you like to edit?", comment: "Title displayed in popup when user has the option to load unsaved changes")

let saveDateFormatted = dateAndTime(for: saveDate)
let autosaveDateFormatted = dateAndTime(for: autosaveDate)
let message = String(format: NSLocalizedString("You recently made changes to this post but didn't save them. Choose a version to load:\n\nFrom this device\nSaved on %@\n\nFrom another device\nSaved on %@\n", comment: "Message displayed in popup when user has the option to load unsaved changes. \n is a placeholder for a new line, and the two %@ are placeholders for the date of last save on this device, and date of last autosave on another device, respectively."), saveDateFormatted, autosaveDateFormatted)

let loadSaveButtonTitle = NSLocalizedString("From this device", comment: "Button title displayed in popup indicating date of change on device")
let fromAutosaveButtonTitle = NSLocalizedString("From another device", comment: "Button title displayed in popup indicating date of change on another device")

let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: loadSaveButtonTitle, style: .default) { _ in
didTapOption(false)
})
alertController.addAction(UIAlertAction(title: fromAutosaveButtonTitle, style: .default) { _ in
didTapOption(true)
})

return alertController
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -518,10 +518,8 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe
presentAlertForPostBeingUploaded()
return
}
let editor = EditPostViewController(post: post)
editor.modalPresentationStyle = .fullScreen
present(editor, animated: false)
WPAppAnalytics.track(.postListEditAction, withProperties: propertiesForAnalytics(), with: apost)

PostListEditorPresenter.handle(post: post, in: self)
}

func presentAlertForPostBeingUploaded() {
Expand Down
2 changes: 1 addition & 1 deletion WordPress/Classes/WordPress.xcdatamodeld/.xccurrentversion
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>WordPress 93.xcdatamodel</string>
<string>WordPress 94.xcdatamodel</string>
</dict>
</plist>
Loading