Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,8 @@ private extension CustomPostEditorViewController {
}

func showPostSettings() {
let viewModel = PostSettingsViewModel(editorService: editorService, blog: blog)
let provider = CustomPostSettingsDataProvider(editorService: editorService, blog: blog)
let viewModel = PostSettingsViewModel(provider: provider)
viewModel.onEditorPostSaved = { /* No-op: shared editorService is already up-to-date */ }
let settingsVC = PostSettingsViewController(viewModel: viewModel)
let navigation = UINavigationController(rootViewController: settingsVC)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ extension PostEditor {
func displayPostSettings() {
// Use the new SwiftUI-based Post Settings
let originalFeaturedImageID = post.featuredImage?.mediaID
let viewModel = PostSettingsViewModel(post: post)
let provider = AbstractPostSettingsDataProvider(post: post)
let viewModel = PostSettingsViewModel(provider: provider)
viewModel.onEditorPostSaved = { [weak self] in
self?.didSavePostSettings(originalFeaturedImageID: originalFeaturedImageID)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import Foundation
import BuildSettingsKit
import WordPressData
import WordPressKit

@MainActor
final class AbstractPostSettingsDataProvider: PostSettingsDataProvider {
let post: AbstractPost
let supportsJetpackMetadata = true
let hasTermNames = true

var blog: Blog {
post.blog
}

var capabilities: PostSettingsCapabilities {
post is Post ? .post() : .page()
}

var postContent: String {
post.content ?? ""
}

var navigationTitle: String {
isPost ? Strings.postSettingsTitle : Strings.pageSettingsTitle
}

var isScheduled: Bool {
post.getOriginal().status == .scheduled
}

var isDraftOrPending: Bool {
post.getOriginal().isStatus(in: [.draft, .pending])
}

var isPost: Bool {
post is Post
}

var authorFallbackDisplayName: String {
post.author?.makePlainText() ?? ""
}

var suggestedSlug: String? {
post.suggested_slug
}

var permalinkTemplate: String? {
post.permalinkTemplateURL
}

var lastEditedText: String? {
guard let date = post.dateModified ?? post.dateCreated else {
return nil
}
return date.toMediumString()
}

var postID: Int? {
guard let postID = post.postID?.intValue, postID > 0 else {
return nil
}
return postID
}

var hasRemote: Bool {
post.hasRemote()
}

var isDeleted: Bool {
guard let context = post.managedObjectContext else {
return true
}
return (try? context.existingObject(with: post.objectID)) == nil
}

func resolveDisplayedCategories(for settings: PostSettings) -> [String] {
settings.getCategoryNames(for: post)
}

func customTaxonomies() -> [SiteTaxonomy] {
let postType: String? = switch post {
case is Post: "post"
case is Page: "page"
default: nil
}
guard let postType else {
return []
}
let taxonomies = try? blog.taxonomies
.filter {
$0.slug != "post_tag" && $0.slug != "category" && $0.supportedPostTypes.contains(postType)
}
.sorted(using: KeyPathComparator(\.name))
return taxonomies ?? []
}

func parentPageText(for pageID: Int?) -> String? {
guard let page = post as? Page,
let context = page.managedObjectContext,
let pageID else {
return nil
}
return Page.parentPageText(in: context, parentID: NSNumber(value: pageID))
}

func resolveTerms(in settings: inout PostSettings) async {
let pendingNames = settings.tags.filter { $0.id == 0 }.map(\.name)
guard !pendingNames.isEmpty else {
return
}

let service = TagsService(blog: blog)
let resolved = await service.resolveTerms(named: pendingNames)
for (name, existing) in resolved {
if let index = settings.tags.firstIndex(where: { $0.name == name }) {
settings.tags[index] = PostSettings.Term(id: Int(existing.id), name: existing.name)
}
}
}

func suggestedTags() async throws -> [String] {
try await TagSuggestionsService().getSuggestedTags(for: post)
}

var isEligibleForSocialSharing: Bool {
guard let post = post as? Post else {
return false
}
return BuildSettings.current.brand == .jetpack
&& RemoteFeatureFlag.jetpackSocialImprovements.enabled()
&& post.status != .publishPrivate
&& !getPublicizeServices().isEmpty
&& blog.supports(.publicize)
}

init(post: AbstractPost) {
self.post = post
}

func makeSettings() -> PostSettings {
PostSettings(from: post)
}

func makeFeaturedImageViewModel() -> PostSettingsFeaturedImageViewModel? {
PostSettingsFeaturedImageViewModel(post: post)
}

func applyLocally(settings: PostSettings) {
settings.apply(to: post)
}

func save(settings: PostSettings) async throws {
let coordinator = PostCoordinator.shared
if coordinator.isSyncAllowed(for: post) && post.status == settings.status {
let revision = post.createRevision()
settings.apply(to: revision)
coordinator.setNeedsSync(for: revision)
} else {
let changes = settings.makeUpdateParameters(from: post)
try await coordinator.save(post, changes: changes)
}
}

func publish(settings: PostSettings) async throws {
let changes = settings.makeUpdateParameters(from: post)
try await PostCoordinator.shared.publish(post.getOriginal(), parameters: changes)
}

// MARK: - Private

private func getPublicizeServices() -> [PublicizeService] {
let context = ContextManager.shared.mainContext
return (try? PublicizeService.allSupportedServices(in: context)) ?? []
}
}

// MARK: - Localized Strings

private enum Strings {
static let postSettingsTitle = NSLocalizedString(
"postSettings.navigationTitle.post",
value: "Post Settings",
comment: "The title of the Post Settings screen."
)

static let pageSettingsTitle = NSLocalizedString(
"postSettings.navigationTitle.page",
value: "Page Settings",
comment: "The title of the Page Settings screen."
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import Foundation
import WordPressData

@MainActor
final class CustomPostSettingsDataProvider: PostSettingsDataProvider {
let blog: Blog
let editorService: CustomPostEditorService

// FIXME: meta support missing in AnyPostWithEditContext

Check warning on line 9 in WordPress/Classes/ViewRelated/Post/PostSettings/CustomPostSettingsDataProvider.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Take the required action to fix the issue indicated by this "FIXME" comment.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-iOS&issues=AZzcaexojGOhYnChnbrV&open=AZzcaexojGOhYnChnbrV&pullRequest=25363
let supportsJetpackMetadata = false
// FIXME: social sharing support missing in AnyPostWithEditContext

Check warning on line 11 in WordPress/Classes/ViewRelated/Post/PostSettings/CustomPostSettingsDataProvider.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Take the required action to fix the issue indicated by this "FIXME" comment.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-iOS&issues=AZzcaexojGOhYnChnbrW&open=AZzcaexojGOhYnChnbrW&pullRequest=25363
let isEligibleForSocialSharing = false
let hasTermNames = false

var capabilities: PostSettingsCapabilities {
PostSettingsCapabilities(from: editorService.details)
}

var postContent: String {
editorService.post?.content.raw ?? ""
}

var navigationTitle: String {
String.localizedStringWithFormat(
Strings.customPostSettingsTitle,
editorService.details.name
)
}

var isScheduled: Bool {
editorService.post?.status == .future
}

var isDraftOrPending: Bool {
if let post = editorService.post {
return post.status == .draft || post.status == .pending
}
return true
}

var isPost: Bool {
editorService.details.slug == "post"
}

var authorFallbackDisplayName: String {
""
}

var suggestedSlug: String? {
editorService.post?.generatedSlug
}

var permalinkTemplate: String? {
editorService.post?.permalinkTemplate
}

var lastEditedText: String? {
editorService.post?.modifiedGmt.toMediumString()
}

var postID: Int? {
guard let id = editorService.post?.id else { return nil }
return id > 0 ? Int(id) : nil
}

var hasRemote: Bool {
editorService.post != nil
}

var isDeleted: Bool {
false
}

func resolveDisplayedCategories(for settings: PostSettings) -> [String] {
settings.getCategoryNames(for: blog)
}

func customTaxonomies() -> [SiteTaxonomy] {
editorService.taxonomies
}

func resolveTerms(in settings: inout PostSettings) async {
do {
let tagsService = AnyTermService(client: editorService.client, endpoint: .tags)
settings.tags = try await TermResolutionService(taxonomyService: tagsService)
.resolveNames(for: settings.tags)

for taxonomy in editorService.taxonomies {
guard let slugTerms = settings.otherTerms[taxonomy.slug] else { continue }
let termService = AnyTermService(client: editorService.client, endpoint: taxonomy.endpoint)
settings.otherTerms[taxonomy.slug] = try await TermResolutionService(taxonomyService: termService)
.resolveNames(for: slugTerms)
}
} catch {
// TODO: We need better error handling

Check warning on line 95 in WordPress/Classes/ViewRelated/Post/PostSettings/CustomPostSettingsDataProvider.swift

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=wordpress-mobile_WordPress-iOS&issues=AZzcaexojGOhYnChnbrX&open=AZzcaexojGOhYnChnbrX&pullRequest=25363
Loggers.app.log(level: .error, "Failed to resolve taxonomy terms: \(error)")
}
}

init(editorService: CustomPostEditorService, blog: Blog) {
self.editorService = editorService
self.blog = blog
}

func makeSettings() -> PostSettings {
var initialSettings = editorService.settings
// Resolve author display name from Blog's cached authors
if let authorId = initialSettings.author?.id,
let authors = blog.authors,
let author = authors.first(where: { $0.userID.intValue == authorId }) {
initialSettings.author = PostSettings.Author(
id: authorId,
displayName: author.displayName ?? "–",
avatarURL: author.avatarURL.flatMap(URL.init)
)
}
return initialSettings
}

func makeFeaturedImageViewModel() -> PostSettingsFeaturedImageViewModel? {
guard capabilities.supportsFeaturedImage else { return nil }

let initialSettings = editorService.settings
let featuredImage = initialSettings.featuredImageID.flatMap {
Media.existingOrStubMediaWith(
mediaID: NSNumber(value: $0),
inBlog: blog
)
}
return PostSettingsFeaturedImageViewModel(
blog: blog,
featuredImage: featuredImage
)
}

func applyLocally(settings: PostSettings) {
editorService.applyLocally(settings: settings)
}

func save(settings: PostSettings) async throws {
try await editorService.save(settings: settings, publish: false)
}

func publish(settings: PostSettings) async throws {
try await editorService.save(settings: settings, publish: true)
}
}

// MARK: - Localized Strings

private enum Strings {
static let customPostSettingsTitle = NSLocalizedString(
"postSettings.navigationTitle.customPostType",
value: "%1$@ Settings",
comment: "The title of the Post Settings screen for custom post types. %1$@ is the post type name."
)
}
Loading