-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Offline Mode: Sync Publishing #22689
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
Changes from all commits
5c34edd
ababbc2
73ac370
40c04a3
8047234
7f54c38
fb5230e
80ebb4f
66a865c
5205217
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import UIKit | ||
|
||
extension PostCoordinator { | ||
static func makePublishSuccessNotice(for post: AbstractPost) -> Notice { | ||
var message: String { | ||
let title = post.titleForDisplay() ?? "" | ||
if !title.isEmpty { | ||
return title | ||
} | ||
return post.blog.displayURL as String? ?? "" | ||
} | ||
return Notice(title: Strings.publishSuccessTitle(for: post), | ||
message: message, | ||
feedbackType: .success, | ||
notificationInfo: makePublishSuccessNotificationInfo(for: post), | ||
actionTitle: Strings.view, | ||
actionHandler: { _ in | ||
PostNoticeNavigationCoordinator.presentPostEpilogue(for: post) | ||
}) | ||
} | ||
|
||
private static func makePublishSuccessNotificationInfo(for post: AbstractPost) -> NoticeNotificationInfo { | ||
var title: String { | ||
let title = post.titleForDisplay() ?? "" | ||
guard !title.isEmpty else { | ||
return Strings.publishSuccessTitle(for: post) | ||
} | ||
return "“\(title)” \(Strings.publishSuccessTitle(for: post))" | ||
} | ||
var body: String { | ||
post.blog.displayURL as String? ?? "" | ||
} | ||
return NoticeNotificationInfo( | ||
identifier: UUID().uuidString, | ||
categoryIdentifier: InteractiveNotificationsManager.NoteCategoryDefinition.postUploadSuccess.rawValue, | ||
title: title, | ||
body: body, | ||
userInfo: [ | ||
PostNoticeUserInfoKey.postID: post.objectID.uriRepresentation().absoluteString | ||
]) | ||
} | ||
|
||
static func makePublishFailureNotice(for post: AbstractPost, error: Error) -> Notice { | ||
return Notice( | ||
title: Strings.uploadFailed, | ||
message: error.localizedDescription, | ||
feedbackType: .error, | ||
notificationInfo: makePublishFailureNotificationInfo(for: post, error: error) | ||
) | ||
} | ||
|
||
private static func makePublishFailureNotificationInfo(for post: AbstractPost, error: Error) -> NoticeNotificationInfo { | ||
var title: String { | ||
let title = post.titleForDisplay() ?? "" | ||
guard !title.isEmpty else { | ||
return Strings.uploadFailed | ||
} | ||
return "“\(title)” \(Strings.uploadFailed)" | ||
} | ||
return NoticeNotificationInfo( | ||
identifier: UUID().uuidString, | ||
categoryIdentifier: nil, | ||
title: title, | ||
body: error.localizedDescription | ||
) | ||
} | ||
} | ||
|
||
private enum Strings { | ||
static let view = NSLocalizedString("postNotice.view", value: "View", comment: "Button title. Displays a summary / sharing screen for a specific post.") | ||
|
||
static let uploadFailed = NSLocalizedString("postNotice.uploadFailed", value: "Upload failed", comment: "A post upload failed notification.") | ||
|
||
static func publishSuccessTitle(for post: AbstractPost, isFirstTimePublish: Bool = true) -> String { | ||
switch post { | ||
case let post as Post: | ||
switch post.status { | ||
case .draft: | ||
return NSLocalizedString("postNotice.postDraftCreated", value: "Post draft uploaded", comment: "Title of notification displayed when a post has been successfully saved as a draft.") | ||
case .scheduled: | ||
return NSLocalizedString("postNotice.postScheduled", value: "Post scheduled", comment: "Title of notification displayed when a post has been successfully scheduled.") | ||
case .pending: | ||
return NSLocalizedString("postNotice.postPendingReview", value: "Post pending review", comment: "Title of notification displayed when a post has been successfully saved as a draft.") | ||
default: | ||
if isFirstTimePublish { | ||
return NSLocalizedString("postNotice.postPublished", value: "Post published", comment: "Title of notification displayed when a post has been successfully published.") | ||
} else { | ||
return NSLocalizedString("postNotice.postUpdated", value: "Post updated", comment: "Title of notification displayed when a post has been successfully updated.") | ||
} | ||
} | ||
case let page as Page: | ||
switch page.status { | ||
case .draft: | ||
return NSLocalizedString("postNotice.pageDraftCreated", value: "Page draft uploaded", comment: "Title of notification displayed when a page has been successfully saved as a draft.") | ||
case .scheduled: | ||
return NSLocalizedString("postNotice.pageScheduled", value: "Page scheduled", comment: "Title of notification displayed when a page has been successfully scheduled.") | ||
case .pending: | ||
return NSLocalizedString("postNotice.pagePending", value: "Page pending review", comment: "Title of notification displayed when a page has been successfully saved as a draft.") | ||
default: | ||
if isFirstTimePublish { | ||
return NSLocalizedString("postNotice.pagePublished", value: "Page published", comment: "Title of notification displayed when a page has been successfully published.") | ||
} else { | ||
return NSLocalizedString("postNotice.pageUpdated", value: "Page updated", comment: "Title of notification displayed when a page has been successfully updated.") | ||
} | ||
} | ||
default: | ||
assertionFailure("Unexpected post type: \(post)") | ||
return "" | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import Aztec | ||
import Foundation | ||
import WordPressKit | ||
import WordPressFlux | ||
|
||
protocol PostCoordinatorDelegate: AnyObject { | ||
|
@@ -15,7 +16,7 @@ class PostCoordinator: NSObject { | |
|
||
@objc static let shared = PostCoordinator() | ||
|
||
private let coreDataStack: CoreDataStack | ||
private let coreDataStack: CoreDataStackSwift | ||
|
||
private var mainContext: NSManagedObjectContext { | ||
coreDataStack.mainContext | ||
|
@@ -41,7 +42,7 @@ class PostCoordinator: NSObject { | |
mediaCoordinator: MediaCoordinator? = nil, | ||
failedPostsFetcher: FailedPostsFetcher? = nil, | ||
actionDispatcherFacade: ActionDispatcherFacade = ActionDispatcherFacade(), | ||
coreDataStack: CoreDataStack = ContextManager.sharedInstance()) { | ||
coreDataStack: CoreDataStackSwift = ContextManager.sharedInstance()) { | ||
self.coreDataStack = coreDataStack | ||
|
||
let mainContext = self.coreDataStack.mainContext | ||
|
@@ -94,6 +95,7 @@ class PostCoordinator: NSObject { | |
} | ||
} | ||
|
||
/// - note: Deprecated (kahu-offline-mode) (See PostCoordinator.publish) | ||
func publish(_ post: AbstractPost) { | ||
if post.status == .draft { | ||
post.status = .publish | ||
|
@@ -109,6 +111,50 @@ class PostCoordinator: NSObject { | |
save(post) | ||
} | ||
|
||
/// Publishes the post according to the current settings and user capabilities. | ||
/// | ||
/// - warning: Before publishing, ensure that the media for the post got | ||
/// uploaded. Managing media is not the responsibility of `PostRepository.` | ||
/// | ||
/// - warning: Work-in-progress (kahu-offline-mode) | ||
@MainActor | ||
func _publish(_ post: AbstractPost) async throws { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm underscoring the methods that are work-in-progress and are only designed to be used in the scope of the project. It also helps avoid the name conflicts. |
||
let parameters = PostHelper.remotePost(with: post) | ||
if post.status == .draft { | ||
parameters.status = PostStatusPublish | ||
parameters.date = Date() | ||
} else { | ||
// Publish according to the currrent post settings: private, scheduled, etc. | ||
} | ||
do { | ||
let repository = PostRepository(coreDataStack: coreDataStack) | ||
let post = try await repository._upload(parameters, for: post) | ||
didPublish(post) | ||
show(PostCoordinator.makePublishSuccessNotice(for: post)) | ||
} catch { | ||
show(PostCoordinator.makePublishFailureNotice(for: post, error: error)) | ||
throw error | ||
} | ||
} | ||
|
||
@MainActor | ||
private func didPublish(_ post: AbstractPost) { | ||
if post.status == .publish { | ||
QuickStartTourGuide.shared.complete(tour: QuickStartPublishTour(), silentlyForBlog: post.blog) | ||
} | ||
if post.status == .scheduled { | ||
notifyNewPostScheduled() | ||
} else if post.status == .publish { | ||
notifyNewPostPublished() | ||
} | ||
SearchManager.shared.indexItem(post) | ||
AppRatingUtility.shared.incrementSignificantEvent() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everything non-editor related is now handled by Noting that it no longer sets |
||
} | ||
|
||
private func show(_ notice: Notice) { | ||
actionDispatcherFacade.dispatch(NoticeAction.post(notice)) | ||
} | ||
|
||
func moveToDraft(_ post: AbstractPost) { | ||
post.status = .draft | ||
save(post) | ||
|
@@ -233,6 +279,7 @@ class PostCoordinator: NSObject { | |
Post.refreshStatus(with: coreDataStack) | ||
} | ||
|
||
/// - note: Deprecated (kahu-offline-mode) | ||
private func upload(post: AbstractPost, forceDraftIfCreating: Bool, completion: ((Result<AbstractPost, Error>) -> ())? = nil) { | ||
mainService.uploadPost(post, forceDraftIfCreating: forceDraftIfCreating, success: { [weak self] uploadedPost in | ||
guard let uploadedPost = uploadedPost else { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import Foundation | ||
import WordPressKit | ||
|
||
extension PostServiceRemote { | ||
func update(_ post: RemotePost) async throws -> RemotePost { | ||
try await withUnsafeThrowingContinuation { continuation in | ||
update(post, success: { | ||
assert($0 != nil) | ||
continuation.resume(returning: $0 ?? post) | ||
}, failure: { error in | ||
continuation.resume(throwing: error ?? URLError(.unknown)) | ||
}) | ||
} | ||
} | ||
|
||
func create(_ post: RemotePost) async throws -> RemotePost { | ||
try await withUnsafeThrowingContinuation { continuation in | ||
createPost(post, success: { | ||
assert($0 != nil) | ||
continuation.resume(returning: $0 ?? post) | ||
}, failure: { error in | ||
continuation.resume(throwing: error ?? URLError(.unknown)) | ||
}) | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest using this marker for anything that's scheduled for removal once the project is live. I'm not "officially"deprecating these APIs to avoid polluting the project with warnings.