Skip to content

Commit 358388c

Browse files
authored
Custom Post: Support update parent page (#25398)
* Decouple PostSettingsViewModelProtocol from Page * Show the "Parent Page" settings * Show selected post * Update page picker UI * Fetch parent page title * Add WpService property * Fix unit tests compilation issues * Fix merge conflict resolution
1 parent 8924e13 commit 358388c

15 files changed

Lines changed: 324 additions & 80 deletions

Tests/KeystoneTests/Tests/Features/Posts/CustomPostEditorServiceTests.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import Testing
22
import Foundation
33
import WordPressAPI
44
import WordPressAPIInternal
5-
import WordPressCore
5+
66
@testable import WordPress
7+
@testable import WordPressCore
78
@testable import WordPressData
89

910
@MainActor
@@ -166,14 +167,14 @@ private func makeService(
166167
api: api,
167168
siteURL: URL(string: "https://example.com")!
168169
)
169-
let postService = PostService(noHandle: .init())
170+
let wpService = try api.createSelfHostedService(cache: .bootstrap())
170171

171172
return CustomPostEditorService(
172173
blog: blog,
173174
post: post,
174175
details: makePostTypeDetails(),
175176
client: client,
176-
service: postService
177+
wpService: wpService
177178
)
178179
}
179180

WordPress/Classes/ViewRelated/CustomPostTypes/CustomPostEditor.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,20 @@ import WordPressAPI
66
import WordPressAPIInternal
77

88
struct CustomPostEditor: View {
9-
let service: WordPressAPIInternal.PostService
9+
let wpService: WpService
1010
let client: WordPressClient
1111
let post: AnyPostWithEditContext?
1212
let details: PostTypeDetailsWithEditContext
1313
let blog: Blog
1414

1515
var body: some View {
16-
ViewControllerWrapper(service: service, client: client, post: post, details: details, blog: blog)
16+
ViewControllerWrapper(wpService: wpService, client: client, post: post, details: details, blog: blog)
1717
.ignoresSafeArea()
1818
}
1919
}
2020

2121
private struct ViewControllerWrapper: UIViewControllerRepresentable {
22-
let service: WordPressAPIInternal.PostService
22+
let wpService: WpService
2323
let client: WordPressClient
2424
let post: AnyPostWithEditContext?
2525
let details: PostTypeDetailsWithEditContext
@@ -29,7 +29,7 @@ private struct ViewControllerWrapper: UIViewControllerRepresentable {
2929
var dismiss: DismissAction
3030

3131
func makeUIViewController(context: Context) -> UIViewController {
32-
let viewController = CustomPostEditorViewController(blog: blog, service: service, client: client, post: post, details: details) {
32+
let viewController = CustomPostEditorViewController(blog: blog, wpService: wpService, client: client, post: post, details: details) {
3333
dismiss()
3434
}
3535
return UINavigationController(rootViewController: viewController)

WordPress/Classes/ViewRelated/CustomPostTypes/CustomPostEditorService.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class CustomPostEditorService {
2727
private var state: State
2828
let details: PostTypeDetailsWithEditContext
2929
let client: WordPressClient
30-
let service: WordPressAPIInternal.PostService
30+
let wpService: WpService
3131
let taxonomies: [SiteTaxonomy]
3232
private var initialSettings: PostSettings
3333

@@ -68,7 +68,7 @@ class CustomPostEditorService {
6868
post: AnyPostWithEditContext?,
6969
details: PostTypeDetailsWithEditContext,
7070
client: WordPressClient,
71-
service: WordPressAPIInternal.PostService
71+
wpService: WpService
7272
) {
7373
if let post {
7474
self.state = .existingPost(post)
@@ -77,7 +77,7 @@ class CustomPostEditorService {
7777
}
7878
self.details = details
7979
self.client = client
80-
self.service = service
80+
self.wpService = wpService
8181

8282
let capabilities = PostSettingsCapabilities(from: details)
8383
// At the moment, category & tags are separated from custom taxonomies. We can unify them as taxonomies later,
@@ -192,7 +192,7 @@ class CustomPostEditorService {
192192
guard try await !hasBeenModified(post: post) else { throw PostUpdateError.conflicts }
193193

194194
let endpoint = details.toPostEndpointType()
195-
let updatedPost = try await service.updatePost(endpointType: endpoint, postId: post.id, params: params)
195+
let updatedPost = try await wpService.posts().updatePost(endpointType: endpoint, postId: post.id, params: params)
196196
state = .existingPost(updatedPost)
197197
initialSettings = settings
198198

@@ -203,7 +203,7 @@ class CustomPostEditorService {
203203
@discardableResult
204204
private func create(params: PostCreateParams) async throws -> AnyPostWithEditContext {
205205
let endpoint = details.toPostEndpointType()
206-
let createdPost = try await service.createPost(endpointType: endpoint, params: params)
206+
let createdPost = try await wpService.posts().createPost(endpointType: endpoint, params: params)
207207
state = .existingPost(createdPost)
208208
initialSettings = settings
209209
return createdPost

WordPress/Classes/ViewRelated/CustomPostTypes/CustomPostListView.swift

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ struct CustomPostListView<Header: View>: View {
1515
let details: PostTypeDetailsWithEditContext
1616
let client: WordPressClient
1717
let mediaHost: MediaHost?
18+
let showsPostActions: Bool
19+
let selectedPostID: Int64?
1820
let onSelectPost: (AnyPostWithEditContext) -> Void
1921
@ViewBuilder let header: () -> Header
2022

@@ -23,13 +25,17 @@ struct CustomPostListView<Header: View>: View {
2325
details: PostTypeDetailsWithEditContext,
2426
client: WordPressClient,
2527
mediaHost: MediaHost? = nil,
28+
showsPostActions: Bool = true,
29+
selectedPostID: Int64? = nil,
2630
onSelectPost: @escaping (AnyPostWithEditContext) -> Void
2731
) where Header == EmptyView {
2832
self.viewModel = viewModel
2933
self.details = details
3034
self.client = client
31-
self.onSelectPost = onSelectPost
3235
self.mediaHost = mediaHost
36+
self.showsPostActions = showsPostActions
37+
self.selectedPostID = selectedPostID
38+
self.onSelectPost = onSelectPost
3339
self.header = { EmptyView() }
3440
}
3541

@@ -38,14 +44,18 @@ struct CustomPostListView<Header: View>: View {
3844
details: PostTypeDetailsWithEditContext,
3945
client: WordPressClient,
4046
mediaHost: MediaHost? = nil,
47+
showsPostActions: Bool = true,
48+
selectedPostID: Int64? = nil,
4149
onSelectPost: @escaping (AnyPostWithEditContext) -> Void,
4250
@ViewBuilder header: @escaping () -> Header
4351
) {
4452
self.viewModel = viewModel
4553
self.details = details
4654
self.client = client
47-
self.onSelectPost = onSelectPost
4855
self.mediaHost = mediaHost
56+
self.showsPostActions = showsPostActions
57+
self.selectedPostID = selectedPostID
58+
self.onSelectPost = onSelectPost
4959
self.header = header
5060
}
5161

@@ -57,6 +67,8 @@ struct CustomPostListView<Header: View>: View {
5767
client: client,
5868
onSelectPost: onSelectPost,
5969
mediaHost: mediaHost,
70+
showsPostActions: showsPostActions,
71+
selectedPostID: selectedPostID,
6072
indentationMap: viewModel.indentationMap,
6173
header: header
6274
)
@@ -122,6 +134,8 @@ private struct PaginatedList<Header: View>: View {
122134
let client: WordPressClient?
123135
let onSelectPost: (AnyPostWithEditContext) -> Void
124136
let mediaHost: MediaHost?
137+
let showsPostActions: Bool
138+
let selectedPostID: Int64?
125139
let indentationMap: CustomPostListViewModel.IndentationMap
126140
@ViewBuilder let header: () -> Header
127141

@@ -135,6 +149,8 @@ private struct PaginatedList<Header: View>: View {
135149
client: WordPressClient? = nil,
136150
onSelectPost: @escaping (AnyPostWithEditContext) -> Void,
137151
mediaHost: MediaHost? = nil,
152+
showsPostActions: Bool = true,
153+
selectedPostID: Int64? = nil,
138154
indentationMap: CustomPostListViewModel.IndentationMap = [:]
139155
) where Header == EmptyView {
140156
self.viewModel = viewModel
@@ -143,6 +159,8 @@ private struct PaginatedList<Header: View>: View {
143159
self.client = client
144160
self.onSelectPost = onSelectPost
145161
self.mediaHost = mediaHost
162+
self.showsPostActions = showsPostActions
163+
self.selectedPostID = selectedPostID
146164
self.indentationMap = indentationMap
147165
self.header = { EmptyView() }
148166
}
@@ -154,6 +172,8 @@ private struct PaginatedList<Header: View>: View {
154172
client: WordPressClient? = nil,
155173
onSelectPost: @escaping (AnyPostWithEditContext) -> Void,
156174
mediaHost: MediaHost? = nil,
175+
showsPostActions: Bool = true,
176+
selectedPostID: Int64? = nil,
157177
indentationMap: CustomPostListViewModel.IndentationMap = [:],
158178
@ViewBuilder header: @escaping () -> Header
159179
) {
@@ -163,6 +183,8 @@ private struct PaginatedList<Header: View>: View {
163183
self.client = client
164184
self.onSelectPost = onSelectPost
165185
self.mediaHost = mediaHost
186+
self.showsPostActions = showsPostActions
187+
self.selectedPostID = selectedPostID
166188
self.indentationMap = indentationMap
167189
self.header = header
168190
}
@@ -197,7 +219,9 @@ private struct PaginatedList<Header: View>: View {
197219
client: client,
198220
onSelectPost: onSelectPost,
199221
mediaHost: mediaHost,
200-
viewModel: viewModel
222+
viewModel: viewModel,
223+
showsPostActions: showsPostActions,
224+
selectedPostID: selectedPostID
201225
)
202226
.task {
203227
await onRowAppear(item: item)
@@ -213,6 +237,8 @@ private struct PaginatedList<Header: View>: View {
213237
onSelectPost: onSelectPost,
214238
mediaHost: mediaHost,
215239
viewModel: viewModel,
240+
showsPostActions: showsPostActions,
241+
selectedPostID: selectedPostID,
216242
indentationLevel: indentationMap[item.id]?.indentationLevel ?? 0,
217243
showSubdirectoryIcon: showSubdirectoryIcon(at: index)
218244
)
@@ -280,6 +306,8 @@ private struct ForEachContent: View {
280306
let onSelectPost: (AnyPostWithEditContext) -> Void
281307
let mediaHost: MediaHost?
282308
@ObservedObject var viewModel: CustomPostListViewModel
309+
var showsPostActions: Bool = true
310+
var selectedPostID: Int64?
283311

284312
var body: some View {
285313
switch item.state {
@@ -288,29 +316,40 @@ private struct ForEachContent: View {
288316
case .loaded(let fullPost, _):
289317
if let post = item.post {
290318
let isPending = viewModel.pendingPostIDs.contains(item.id)
291-
Button {
319+
let button = Button {
292320
onSelectPost(fullPost)
293321
} label: {
294-
PostContent(post: post, client: client, mediaHost: mediaHost)
295-
}
296-
.buttonStyle(.plain)
297-
.contextMenu {
298-
if !isPending {
299-
PostActionMenuContent(post: fullPost, viewModel: viewModel)
322+
HStack {
323+
PostContent(post: post, client: client, mediaHost: mediaHost)
324+
if selectedPostID == fullPost.id {
325+
Image(systemName: "checkmark.circle.fill")
326+
.foregroundStyle(.tint)
327+
}
300328
}
301329
}
302-
.overlay(alignment: .topTrailing) {
303-
if isPending {
304-
ProgressView()
305-
.frame(width: 28, height: 28)
306-
.offset(y: -6)
307-
} else {
308-
PostActionMenu(post: fullPost, viewModel: viewModel)
309-
.offset(y: -6)
310-
}
330+
.buttonStyle(.plain)
331+
332+
if isPending {
333+
button
334+
.overlay(alignment: .topTrailing) {
335+
ProgressView()
336+
.frame(width: 28, height: 28)
337+
.offset(y: -6)
338+
}
339+
.opacity(0.4)
340+
.disabled(true)
341+
} else if showsPostActions {
342+
button
343+
.contextMenu {
344+
PostActionMenuContent(post: fullPost, viewModel: viewModel)
345+
}
346+
.overlay(alignment: .topTrailing) {
347+
PostActionMenu(post: fullPost, viewModel: viewModel)
348+
.offset(y: -6)
349+
}
350+
} else {
351+
button
311352
}
312-
.opacity(isPending ? 0.4 : 1)
313-
.disabled(isPending)
314353
}
315354

316355
case .loading:
@@ -340,6 +379,8 @@ private struct ForEachContentWithIndentation: View {
340379
let onSelectPost: (AnyPostWithEditContext) -> Void
341380
let mediaHost: MediaHost?
342381
let viewModel: CustomPostListViewModel
382+
var showsPostActions: Bool = true
383+
var selectedPostID: Int64?
343384
let indentationLevel: Int
344385
let showSubdirectoryIcon: Bool
345386

@@ -357,7 +398,9 @@ private struct ForEachContentWithIndentation: View {
357398
client: client,
358399
onSelectPost: onSelectPost,
359400
mediaHost: mediaHost,
360-
viewModel: viewModel
401+
viewModel: viewModel,
402+
showsPostActions: showsPostActions,
403+
selectedPostID: selectedPostID
361404
)
362405
}
363406
.padding(.leading, CGFloat(max(0, indentationLevel - 1)) * 32)

WordPress/Classes/ViewRelated/CustomPostTypes/CustomPostListViewModel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ final class CustomPostListViewModel: ObservableObject {
292292
post: post,
293293
details: details,
294294
client: client,
295-
service: service.posts()
295+
wpService: service
296296
)
297297
PublishPostViewController.show(
298298
editorService: editorService,
@@ -384,7 +384,7 @@ final class CustomPostListViewModel: ObservableObject {
384384
post: post,
385385
details: details,
386386
client: client,
387-
service: service.posts()
387+
wpService: service
388388
)
389389
let viewModel = CustomPostSettingsViewModel(editorService: editorService, blog: blog, isStandalone: true)
390390
let settingsVC = PostSettingsViewController(viewModel: viewModel)

0 commit comments

Comments
 (0)