Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 4 additions & 5 deletions Modules/Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ let package = Package(
.package(url: "https://github.com/wordpress-mobile/wpxmlrpc", from: "0.9.0"),
.package(url: "https://github.com/wordpress-mobile/NSURL-IDN", revision: "b34794c9a3f32312e1593d4a3d120572afa0d010"),
.package(url: "https://github.com/zendesk/support_sdk_ios", from: "8.0.3"),
.package(url: "https://github.com/wordpress-mobile/GutenbergKit", from: "0.13.1"),
.package(url: "https://github.com/wordpress-mobile/GutenbergKit", revision: "7a48d8756f33e3d25daa1fff5843d5cd6fdda887"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we are ready, we can integrate a GBK prerelease. We'll publish the prerelease via make release VERSION_TYPE=preminor DRY_RUN=true on GBK's trunk branch.

// We can't use wordpress-rs branches nor commits here. Only tags work.
.package(url: "https://github.com/Automattic/wordpress-rs", revision: "alpha-20260122"),
.package(url: "https://github.com/Automattic/wordpress-rs", revision: "alpha-20260203"),
.package(
url: "https://github.com/Automattic/color-studio",
revision: "bf141adc75e2769eb469a3e095bdc93dc30be8de"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import WordPressShared
import Support

extension EditorConfiguration {
init(blog: Blog, postType: String = "post", keychain: KeychainAccessible = KeychainUtils()) {
init(blog: Blog, postType: PostTypeDetails = .post, keychain: KeychainAccessible = KeychainUtils()) {
let selfHostedApiUrl = blog.restApiRootURL ?? blog.url(withPath: "wp-json/")
let applicationPassword = try? blog.getApplicationToken(using: keychain)
let shouldUseWPComRestApi = applicationPassword == nil && blog.isAccessibleThroughWPCom()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ final class CommentGutenbergEditorViewController: UIViewController {
// doesn't need network access
let configuration = EditorConfigurationBuilder(
content: initialContent ?? "",
postType: "comment",
postType: .init(postType: "comment", restBase: "comments"), // FIXME: "comment" is not a post type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we feel compelled to "fix" the post type, we might also consider fixing the site URL and API root. GBK currently requires all of these, but effectively discards them when "offline mode" is enabled.

For context, this comment editor is currently unused.

siteURL: URL(string: "https://offline.local")!,
siteApiRoot: URL(string: "https://offline.local/wp-json")!
)
Expand Down
127 changes: 7 additions & 120 deletions WordPress/Classes/ViewRelated/CustomPostTypes/CustomPostEditor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,135 +5,22 @@ import WordPressData
import WordPressAPI
import WordPressAPIInternal

struct CustomPostEditor: View {
struct CustomPostEditor: UIViewControllerRepresentable {
let client: WordPressClient
let post: AnyPostWithEditContext
let details: PostTypeDetailsWithEditContext
let blog: Blog
let success: () -> Void

private let coordinator = SimpleGBKEditor.EditorCoordinator()

@Environment(\.dismiss) private var dismiss

var body: some View {
NavigationStack {
SimpleGBKEditor(post: post, blog: blog, coordinator: coordinator)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(role: .cancel) {
dismiss()
} label: {
Image(systemName: "xmark")
}
}
ToolbarItem(placement: .primaryAction) {
Button(SharedStrings.Button.save) {
save()
}
}
}
}
}

private func save() {
Task {
SVProgressHUD.show()

do {
guard let (title, content) = try await coordinator.getContent() else { return }

try await update(title: title, content: content)
SVProgressHUD.showSuccess(withStatus: nil)

dismiss()
success()
} catch {
SVProgressHUD.showError(withStatus: error.localizedDescription)
}
}
}

private func hasBeenModified() async throws -> Bool {
let endpoint = postTypeDetailsToPostEndpointType(postTypeDetails: details)
let lastModified = try await client.api.posts
.filterRetrieveWithEditContext(
postEndpointType: endpoint,
postId: post.id,
params: .init(),
fields: [.modified]
)
.data
.modified
return lastModified != post.modified
}

private func update(title: String, content: String) async throws {
// This is a simple way to avoid overwriting others' changes. We can further improve it
// to align with the implementation in `PostRepository`.
guard try await !hasBeenModified() else { throw PostUpdateError.conflicts }

let hasTitle = details.supports.map[.title] == .bool(true)
let params = PostUpdateParams(
title: hasTitle ? title : nil,
content: content,
meta: nil
)
_ = try await client.api
.posts
.update(
postEndpointType: postTypeDetailsToPostEndpointType(postTypeDetails: details),
postId: post.id,
params: params
)
}
}

private struct SimpleGBKEditor: UIViewControllerRepresentable {
class EditorCoordinator {
weak var editor: SimpleGBKViewController?

func getContent() async throws -> (title: String, content: String)? {
try await editor?.getCurrentContent()
}
}

let post: AnyPostWithEditContext
let blog: Blog
let coordinator: EditorCoordinator

func makeCoordinator() -> EditorCoordinator {
coordinator
}
@Environment(\.dismiss)
var dismiss: DismissAction

func makeUIViewController(context: Context) -> UIViewController {
let editor = SimpleGBKViewController(
postID: Int(post.id),
postTitle: post.title?.raw,
content: post.content.raw ?? "",
blog: blog,
postType: post.postType
)
context.coordinator.editor = editor
return editor
let viewController = CustomPostEditorViewController(blog: blog, client: client, post: post, details: details) {
dismiss()
}
return UINavigationController(rootViewController: viewController)
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}

private enum PostUpdateError: LocalizedError {
case conflicts

var errorDescription: String? {
Strings.conflictErrorMessage
}
}

private enum Strings {
static let conflictErrorMessage = NSLocalizedString(
"customPostEditor.error.conflict.message",
value: "The post you are trying to save has been changed in the meantime.",
comment: "Error message shown when the post was modified by another user while editing"
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,7 @@ struct CustomPostMainView: View {
.searchable(text: $searchText)
.fullScreenCover(item: $selectedPost) { post in
// TODO: Check if the post supports Gutenberg first?
CustomPostEditor(client: client, post: post, details: details, blog: blog) {
Task {
_ = try await service.posts().refreshPost(postId: post.id, endpointType: endpoint)
}
}
CustomPostEditor(client: client, post: post, details: details, blog: blog)
}
.navigationTitle(details.labels.itemsList)
.toolbar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ struct CustomPostTypesView: View {
self.types = try await self.collection.loadData()
.compactMap {
let details = $0.data
let endpoint = postTypeDetailsToPostEndpointType(postTypeDetails: details)
let endpoint = details.toPostEndpointType()
if case .custom = endpoint, details.slug != "attachment" {
return (endpoint, details)
}
Expand Down
Loading