diff --git a/Sources/WordPressData/Swift/Blog+Features.swift b/Sources/WordPressData/Swift/Blog+Features.swift index c95f482adf89..9349e3be0601 100644 --- a/Sources/WordPressData/Swift/Blog+Features.swift +++ b/Sources/WordPressData/Swift/Blog+Features.swift @@ -53,6 +53,7 @@ import Foundation case siteMonitoring case publicize case shareButtons + case jetpackNewsletter } extension Blog { @@ -82,7 +83,9 @@ extension Blog { case .customThemes: return supportsRestAPI && isAdmin && !isHostedAtWPcom case .premiumThemes: - return supports(.customThemes) && (planID?.intValue == Self.jetpackProfessionalYearlyPlanId || planID?.intValue == Self.jetpackProfessionalMonthlyPlanId) + return supports(.customThemes) + && (planID?.intValue == Self.jetpackProfessionalYearlyPlanId + || planID?.intValue == Self.jetpackProfessionalMonthlyPlanId) case .private: return isHostedAtWPcom case .sharing: @@ -132,6 +135,8 @@ extension Blog { return supportsPublicize case .shareButtons: return supportsShareButtons + case .jetpackNewsletter: + return supportsJetpackNewsletter } } @@ -171,6 +176,14 @@ private extension Blog { } } + var supportsJetpackNewsletter: Bool { + guard supportsRestAPI else { return false } + if isHostedAtWPcom { + return true + } + return isJetpackModuleActive("subscriptions") + } + var supportsShareButtons: Bool { guard isAdmin, supportsRestAPI else { return false @@ -192,7 +205,8 @@ private extension Blog { return true } if account == nil && !isHostedAtWPcom && selfHostedSiteRestApi != nil - && hasRequiredWordPressVersion("5.5") { + && hasRequiredWordPressVersion("5.5") + { return true } return false @@ -207,7 +221,8 @@ private extension Blog { func hasRequiredJetpackVersion(_ requiredVersion: String) -> Bool { guard supportsRestAPI, !isHostedAtWPcom, - let version = jetpack?.version else { + let version = jetpack?.version + else { return false } return version.compare(requiredVersion, options: .numeric) != .orderedAscending diff --git a/Sources/WordPressData/Swift/PostMetadata.swift b/Sources/WordPressData/Swift/PostMetadata.swift index 27ce43fca15d..1f1702ed0d51 100644 --- a/Sources/WordPressData/Swift/PostMetadata.swift +++ b/Sources/WordPressData/Swift/PostMetadata.swift @@ -20,6 +20,11 @@ public struct PostMetadata: Hashable { self.isJetpackNewsletterEmailDisabled = container.getAdaptiveBool(for: .jetpackNewsletterEmailDisabled) } + public init(accessLevel: JetpackPostAccessLevel?, isJetpackNewsletterEmailDisabled: Bool = false) { + self.accessLevel = accessLevel + self.isJetpackNewsletterEmailDisabled = isJetpackNewsletterEmailDisabled + } + /// Applies the metadata values to the container and returns them /// as metadata values. public func encode(in container: inout PostMetadataContainer) { @@ -28,7 +33,10 @@ public struct PostMetadata: Hashable { container.accessLevel = accessLevel } if previous.isJetpackNewsletterEmailDisabled != isJetpackNewsletterEmailDisabled { - container.setValue(String(describing: isJetpackNewsletterEmailDisabled), for: .jetpackNewsletterEmailDisabled) + container.setValue( + String(describing: isJetpackNewsletterEmailDisabled), + for: .jetpackNewsletterEmailDisabled + ) } } diff --git a/Tests/KeystoneTests/Tests/Features/Posts/CustomPostSettingsViewModelTests.swift b/Tests/KeystoneTests/Tests/Features/Posts/CustomPostSettingsViewModelTests.swift index c73d7b5cb4f4..626a0f083666 100644 --- a/Tests/KeystoneTests/Tests/Features/Posts/CustomPostSettingsViewModelTests.swift +++ b/Tests/KeystoneTests/Tests/Features/Posts/CustomPostSettingsViewModelTests.swift @@ -28,9 +28,11 @@ struct CustomPostSettingsViewModelTests { ) // Sanity: the parsed draft has the disabled connection from the post. - #expect(viewModel.settings.socialSharingDraft?.connectionsByID == [ - "12345": .init(id: "12345", enabled: false) - ]) + #expect( + viewModel.settings.socialSharingDraft?.connectionsByID == [ + "12345": .init(id: "12345", enabled: false) + ] + ) // When: settings is reassigned to a value-equivalent copy (simulating // `resolveTermNames` writing back resolved-but-identical tags). @@ -195,6 +197,41 @@ struct CustomPostSettingsViewModelTests { #expect(viewModel.v2SocialSharing == nil) #expect(viewModel.getSettingsToSave(for: viewModel.settings).socialSharingDraft == nil) } + + // MARK: - shouldShow Jetpack rows + + @Test("shouldShow .jetpackAccessLevel is true for post type when Jetpack newsletter is available") + func shouldShowAccessLevelTrue() throws { + let viewModel = try makeViewModel(postTypeSlug: "post", jetpackNewsletter: true) + #expect(viewModel.shouldShow(.jetpackAccessLevel)) + } + + @Test("shouldShow .jetpackAccessLevel is false for non-post type") + func shouldShowAccessLevelFalseForNonPost() throws { + let viewModel = try makeViewModel(postTypeSlug: "page", jetpackNewsletter: true) + #expect(!viewModel.shouldShow(.jetpackAccessLevel)) + } + + @Test("shouldShow .jetpackAccessLevel is false when Jetpack newsletter is unavailable") + func shouldShowAccessLevelFalseWithoutNewsletter() throws { + let viewModel = try makeViewModel(postTypeSlug: "post", jetpackNewsletter: false) + #expect(!viewModel.shouldShow(.jetpackAccessLevel)) + } + + @Test("shouldShow .jetpackNewsletterEmailOptions is true only in publishing context") + func shouldShowNewsletterTrueOnlyInPublishing() throws { + let publishingVM = try makeViewModel(postTypeSlug: "post", jetpackNewsletter: true, context: .publishing) + #expect(publishingVM.shouldShow(.jetpackNewsletterEmailOptions)) + + let settingsVM = try makeViewModel(postTypeSlug: "post", jetpackNewsletter: true, context: .settings) + #expect(!settingsVM.shouldShow(.jetpackNewsletterEmailOptions)) + } + + @Test("shouldShow .jetpackNewsletterEmailOptions is false for non-post type") + func shouldShowNewsletterFalseForNonPost() throws { + let viewModel = try makeViewModel(postTypeSlug: "page", jetpackNewsletter: true, context: .publishing) + #expect(!viewModel.shouldShow(.jetpackNewsletterEmailOptions)) + } } // MARK: - Test Helpers @@ -295,7 +332,44 @@ private func makeConnectionsService() -> SiteSocialConnectionsService { ) } -private func makePostTypeDetails(supportsPublicize: Bool = true) -> PostTypeDetailsWithEditContext { +@MainActor +private func makeViewModel( + postTypeSlug: String, + jetpackNewsletter: Bool, + context: PostSettingsContext = .settings +) throws -> CustomPostSettingsViewModel { + let coreData = ContextManager.forTesting().mainContext + let builder = BlogBuilder(coreData) + let blog: Blog + if jetpackNewsletter { + blog = builder.withAnAccount().with(modules: ["subscriptions"]).build() + } else { + blog = builder.withAnAccount().build() + } + try coreData.save() + let post = try makePostWithDisabledConnection() + let details = makePostTypeDetails(supportsPublicize: true, slug: postTypeSlug) + let dependencies = try makeServiceDependencies() + let editorService = CustomPostEditorService( + blog: blog, + post: post, + details: details, + client: dependencies.client, + wpService: dependencies.wpService, + initialParams: nil + ) + return CustomPostSettingsViewModel( + editorService: editorService, + blog: blog, + socialConnectionsService: nil, + context: context + ) +} + +private func makePostTypeDetails( + supportsPublicize: Bool = true, + slug: String = "test_post_type" +) -> PostTypeDetailsWithEditContext { var supports: [PostTypeSupports: JsonValue] = [ .title: .bool(true), .editor: .bool(true) @@ -311,11 +385,11 @@ private func makePostTypeDetails(supportsPublicize: Bool = true) -> PostTypeDeta viewable: true, labels: makePostTypeLabels(), name: "Test Post Type", - slug: "test_post_type", + slug: slug, supports: PostTypeSupportsMap(map: supports), hasArchive: .bool(false), taxonomies: [], - restBase: "test_post_type", + restBase: slug, restNamespace: "wp/v2", visibility: PostTypeVisibility(showInNavMenus: true, showUi: true), icon: nil diff --git a/Tests/KeystoneTests/Tests/Features/Posts/PostMetaJetpackNewsletterTests.swift b/Tests/KeystoneTests/Tests/Features/Posts/PostMetaJetpackNewsletterTests.swift new file mode 100644 index 000000000000..2f1bcb2f3061 --- /dev/null +++ b/Tests/KeystoneTests/Tests/Features/Posts/PostMetaJetpackNewsletterTests.swift @@ -0,0 +1,61 @@ +import Testing +import Foundation +import WordPressAPIInternal +import WordPressData + +@testable import WordPress + +struct PostMetaJetpackNewsletterTests { + + // MARK: - Access Level + + @Test("addingJetpackNewsletterAccess round-trips through jetpackNewsletterAccess") + func accessLevelRoundTrip() { + let meta = PostMeta().addingJetpackNewsletterAccess(.subscribers) + #expect(meta.jetpackNewsletterAccess == .subscribers) + } + + @Test("addingJetpackNewsletterAccess supports paid_subscribers") + func accessLevelPaidSubscribers() { + let meta = PostMeta().addingJetpackNewsletterAccess(.paidSubscribers) + #expect(meta.jetpackNewsletterAccess == .paidSubscribers) + } + + @Test("addingJetpackNewsletterAccess with nil clears the value") + func accessLevelClear() { + let meta = PostMeta() + .addingJetpackNewsletterAccess(.subscribers) + .addingJetpackNewsletterAccess(nil) + #expect(meta.jetpackNewsletterAccess == nil) + } + + @Test("jetpackNewsletterAccess returns nil when key is absent") + func accessLevelAbsent() { + #expect(PostMeta().jetpackNewsletterAccess == nil) + } + + @Test("jetpackNewsletterAccess returns nil for unknown raw values") + func accessLevelUnknownRawValue() { + let meta = PostMeta().withValue(key: "_jetpack_newsletter_access", value: .string("not_a_level")) + #expect(meta.jetpackNewsletterAccess == nil) + } + + // MARK: - Email Disabled + + @Test("addingJetpackNewsletterEmailDisabled true round-trips") + func emailDisabledTrueRoundTrip() { + let meta = PostMeta().addingJetpackNewsletterEmailDisabled(true) + #expect(meta.isJetpackNewsletterEmailDisabled) + } + + @Test("addingJetpackNewsletterEmailDisabled false round-trips") + func emailDisabledFalseRoundTrip() { + let meta = PostMeta().addingJetpackNewsletterEmailDisabled(false) + #expect(!meta.isJetpackNewsletterEmailDisabled) + } + + @Test("isJetpackNewsletterEmailDisabled returns false when key is absent") + func emailDisabledAbsent() { + #expect(!PostMeta().isJetpackNewsletterEmailDisabled) + } +} diff --git a/Tests/KeystoneTests/Tests/Features/Posts/PostSettingsTests.swift b/Tests/KeystoneTests/Tests/Features/Posts/PostSettingsTests.swift index 971b765bc8d8..e347a72c33e1 100644 --- a/Tests/KeystoneTests/Tests/Features/Posts/PostSettingsTests.swift +++ b/Tests/KeystoneTests/Tests/Features/Posts/PostSettingsTests.swift @@ -719,6 +719,30 @@ struct PostSettingsTests { #expect(settings.socialSharingDraft == expectedDraft) } + @Test("init(from: AnyPostWithEditContext) populates jetpack newsletter access from meta") + func initFromRestPopulatesAccessLevel() throws { + let meta = PostMeta().addingJetpackNewsletterAccess(.paidSubscribers) + let post = makeRemotePost(meta: meta) + let settings = PostSettings(from: post) + #expect(settings.metadata.accessLevel == .paidSubscribers) + } + + @Test("init(from: AnyPostWithEditContext) populates email-disabled flag from meta") + func initFromRestPopulatesEmailDisabled() throws { + let meta = PostMeta().addingJetpackNewsletterEmailDisabled(true) + let post = makeRemotePost(meta: meta) + let settings = PostSettings(from: post) + #expect(settings.metadata.isJetpackNewsletterEmailDisabled) + } + + @Test("init(from: AnyPostWithEditContext) defaults metadata when meta is nil") + func initFromRestDefaultsMetadata() throws { + let post = makeRemotePost(meta: nil) + let settings = PostSettings(from: post) + #expect(settings.metadata.accessLevel == nil) + #expect(!settings.metadata.isJetpackNewsletterEmailDisabled) + } + @Test("apply(to:) converts terms back to name strings") func testApplyConvertsTermsToNameStrings() { // Given @@ -1046,10 +1070,12 @@ struct PostSettingsTests { let settings = PostSettings(from: params) #expect(settings.socialSharingDraft?.customMessage == "Stored message") - #expect(settings.socialSharingDraft?.connectionsByID == [ - "1": .init(id: "1", enabled: true), - "2": .init(id: "2", enabled: false) - ]) + #expect( + settings.socialSharingDraft?.connectionsByID == [ + "1": .init(id: "1", enabled: true), + "2": .init(id: "2", enabled: false) + ] + ) } @Test("makeCreateParameters clears stored publicize message from empty social draft") @@ -1136,6 +1162,88 @@ struct PostSettingsTests { #expect(settings.publishDate == nil) } + // MARK: - makeUpdateParameters(from: AnyPostWithEditContext) Newsletter Meta Tests + + @Test("makeUpdateParameters(from: AnyPostWithEditContext) omits meta when newsletter settings unchanged") + func updateParamsOmitsMetaWhenUnchanged() throws { + let meta = PostMeta() + .addingJetpackNewsletterAccess(.subscribers) + .addingJetpackNewsletterEmailDisabled(true) + let post = makeRemotePost(meta: meta) + let settings = PostSettings(from: post) + // Sanity: metadata read back correctly. + #expect(settings.metadata.accessLevel == .subscribers) + + let params = settings.makeUpdateParameters(from: post) + #expect(params.meta?.jetpackNewsletterAccess == nil) + #expect(params.meta?.valueForKey(key: "_jetpack_dont_email_post_to_subs") == nil) + } + + @Test("makeUpdateParameters(from: AnyPostWithEditContext) writes access level when changed") + func updateParamsWritesAccessLevelChange() throws { + let post = makeRemotePost(meta: nil) + var settings = PostSettings(from: post) + settings.metadata.accessLevel = .paidSubscribers + + let params = settings.makeUpdateParameters(from: post) + #expect(params.meta?.jetpackNewsletterAccess == .paidSubscribers) + // Email-disabled key should NOT be written because it didn't change. + #expect(params.meta?.valueForKey(key: "_jetpack_dont_email_post_to_subs") == nil) + } + + @Test("makeUpdateParameters(from: AnyPostWithEditContext) writes email-disabled when changed") + func updateParamsWritesEmailDisabledChange() throws { + let post = makeRemotePost(meta: nil) + var settings = PostSettings(from: post) + settings.metadata.isJetpackNewsletterEmailDisabled = true + + let params = settings.makeUpdateParameters(from: post) + #expect(params.meta?.isJetpackNewsletterEmailDisabled == true) + #expect(params.meta?.valueForKey(key: "_jetpack_newsletter_access") == nil) + } + + @Test("makeUpdateParameters(from: AnyPostWithEditContext) clears access level when set to nil") + func updateParamsClearsAccessLevel() throws { + let meta = PostMeta().addingJetpackNewsletterAccess(.subscribers) + let post = makeRemotePost(meta: meta) + var settings = PostSettings(from: post) + settings.metadata.accessLevel = nil + + let params = settings.makeUpdateParameters(from: post) + // A nil access level writes `.null` so the server clears the meta. + #expect(params.meta?.valueForKey(key: "_jetpack_newsletter_access") == JsonValue.null) + } + + // MARK: - makeCreateParameters Newsletter Meta Tests + + @Test("makeCreateParameters emits access level when set") + func createParamsEmitsAccessLevel() throws { + var settings = PostSettings(from: PostCreateParams()) + settings.metadata.accessLevel = .subscribers + + let params = settings.makeCreateParameters(from: PostCreateParams()) + #expect(params.meta?.jetpackNewsletterAccess == .subscribers) + } + + @Test("makeCreateParameters emits email-disabled when true") + func createParamsEmitsEmailDisabled() throws { + var settings = PostSettings(from: PostCreateParams()) + settings.metadata.isJetpackNewsletterEmailDisabled = true + + let params = settings.makeCreateParameters(from: PostCreateParams()) + #expect(params.meta?.isJetpackNewsletterEmailDisabled == true) + } + + @Test("makeCreateParameters omits newsletter meta at defaults") + func createParamsOmitsDefaults() throws { + let settings = PostSettings(from: PostCreateParams()) + // Defaults: accessLevel nil, isJetpackNewsletterEmailDisabled false. + + let params = settings.makeCreateParameters(from: PostCreateParams()) + #expect(params.meta?.valueForKey(key: "_jetpack_newsletter_access") == nil) + #expect(params.meta?.valueForKey(key: "_jetpack_dont_email_post_to_subs") == nil) + } + @Test("PostCreateParams status and date survive round-trip through PostSettings") func testStatusAndDateRoundTripThroughPostSettings() { // Given diff --git a/Tests/WordPressDataTests/PostMetadataTests.swift b/Tests/WordPressDataTests/PostMetadataTests.swift index 05726c70d8e8..12f2f46078d0 100644 --- a/Tests/WordPressDataTests/PostMetadataTests.swift +++ b/Tests/WordPressDataTests/PostMetadataTests.swift @@ -81,6 +81,23 @@ struct PostMetadataTests { // THEN #expect(container.getString(for: .jetpackNewsletterEmailDisabled)?.isEmpty == true) } + + @Test("memberwise init populates accessLevel and isJetpackNewsletterEmailDisabled") + func memberwiseInit() { + let metadata = PostMetadata( + accessLevel: .paidSubscribers, + isJetpackNewsletterEmailDisabled: true + ) + #expect(metadata.accessLevel == .paidSubscribers) + #expect(metadata.isJetpackNewsletterEmailDisabled) + } + + @Test("memberwise init defaults isJetpackNewsletterEmailDisabled to false when omitted") + func memberwiseInitDefaults() { + let metadata = PostMetadata(accessLevel: nil) + #expect(metadata.accessLevel == nil) + #expect(!metadata.isJetpackNewsletterEmailDisabled) + } } private extension PostMetadata { diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/CustomPostSettingsViewModel.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/CustomPostSettingsViewModel.swift index 847280dea9b1..e44bd5a3c52c 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/CustomPostSettingsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/CustomPostSettingsViewModel.swift @@ -295,8 +295,12 @@ final class CustomPostSettingsViewModel: NSObject, ObservableObject, PostSetting func onAppear() {} func shouldShow(_ row: PostSettingsRow) -> Bool { - // FIXME: meta support missing in AnyPostWithEditContext - false + switch row { + case .jetpackAccessLevel: + return isPost && blog.supports(.jetpackNewsletter) + case .jetpackNewsletterEmailOptions: + return isPost && blog.supports(.jetpackNewsletter) && context == .publishing + } } func buttonCancelTapped() { diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/Extensions/PostMeta+JetpackNewsletter.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/Extensions/PostMeta+JetpackNewsletter.swift new file mode 100644 index 000000000000..e17d6b529307 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/Extensions/PostMeta+JetpackNewsletter.swift @@ -0,0 +1,42 @@ +import Foundation +import WordPressAPIInternal +import WordPressData + +extension PostMeta { + /// The Jetpack Newsletter access level stored in this meta, if any. + /// + /// Jetpack registers `_jetpack_newsletter_access` for the built-in `post` + /// type only (see `extensions/blocks/subscriptions/subscriptions.php` in + /// the Jetpack plugin). + var jetpackNewsletterAccess: JetpackPostAccessLevel? { + guard case let .string(raw)? = valueForKey(key: Self.newsletterAccessKey) else { + return nil + } + return JetpackPostAccessLevel(rawValue: raw) + } + + /// Returns a new `PostMeta` with the access level set. Pass `nil` to + /// clear any previously-saved value. + func addingJetpackNewsletterAccess(_ accessLevel: JetpackPostAccessLevel?) -> PostMeta { + let value: JsonValue = accessLevel.map { .string($0.rawValue) } ?? .null + return withValue(key: Self.newsletterAccessKey, value: value) + } + + /// Whether the post is configured to NOT be sent in an email to + /// subscribers. Defaults to `false` when the key is absent, matching + /// `PostMetadataContainer.getAdaptiveBool` semantics. + var isJetpackNewsletterEmailDisabled: Bool { + guard case let .bool(value)? = valueForKey(key: Self.dontEmailKey) else { + return false + } + return value + } + + /// Returns a new `PostMeta` with the "don't email" flag set. + func addingJetpackNewsletterEmailDisabled(_ disabled: Bool) -> PostMeta { + withValue(key: Self.dontEmailKey, value: .bool(disabled)) + } + + private static let newsletterAccessKey = "_jetpack_newsletter_access" + private static let dontEmailKey = "_jetpack_dont_email_post_to_subs" +} diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift index f454f3879b76..51dcee4b2695 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettings.swift @@ -224,8 +224,10 @@ struct PostSettings: Hashable { } self.otherTerms = otherTerms - // FIXME: Post metadata is not supported yet. Require wordpress-rs changes. - metadata = PostMetadata(from: .init()) + metadata = PostMetadata( + accessLevel: post.meta?.jetpackNewsletterAccess, + isJetpackNewsletterEmailDisabled: post.meta?.isJetpackNewsletterEmailDisabled ?? false + ) postFormat = post.format.map { $0.id } isStickyPost = post.sticky ?? false @@ -488,6 +490,19 @@ struct PostSettings: Hashable { } } + let originalMetadata = PostMetadata( + accessLevel: post.meta?.jetpackNewsletterAccess, + isJetpackNewsletterEmailDisabled: post.meta?.isJetpackNewsletterEmailDisabled ?? false + ) + if originalMetadata.accessLevel != self.metadata.accessLevel { + params.meta = (params.meta ?? PostMeta()) + .addingJetpackNewsletterAccess(self.metadata.accessLevel) + } + if originalMetadata.isJetpackNewsletterEmailDisabled != self.metadata.isJetpackNewsletterEmailDisabled { + params.meta = (params.meta ?? PostMeta()) + .addingJetpackNewsletterEmailDisabled(self.metadata.isJetpackNewsletterEmailDisabled) + } + let postParentPageID = post.parent.map { Int($0) } if postParentPageID != self.parentPageID { params.parent = self.parentPageID.map { PostId(Int64($0)) } ?? PostId(0) @@ -552,6 +567,19 @@ struct PostSettings: Hashable { } } + // TODO: When PR 25543 moves new-post state to PostSettings, keep this + // newsletter serialization in makeCreateParameters(taxonomies:). If the + // PostCreateParams restore path remains, it must decode these meta + // fields or local new-post settings reopen with stale defaults. + if metadata.accessLevel != nil { + params.meta = (params.meta ?? PostMeta()) + .addingJetpackNewsletterAccess(metadata.accessLevel) + } + if metadata.isJetpackNewsletterEmailDisabled { + params.meta = (params.meta ?? PostMeta()) + .addingJetpackNewsletterEmailDisabled(true) + } + return params } } diff --git a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsViewModel.swift b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsViewModel.swift index 92fd79b783f4..dfe340668dfa 100644 --- a/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Post/PostSettings/PostSettingsViewModel.swift @@ -104,7 +104,8 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM var postFormatText: String { guard capabilities.supportsPostFormats else { return "" } - return blog.postFormatText(fromSlug: settings.postFormat) ?? NSLocalizedString("Standard", comment: "Default post format") + return blog.postFormatText(fromSlug: settings.postFormat) + ?? NSLocalizedString("Standard", comment: "Default post format") } var timeZone: TimeZone { @@ -204,9 +205,11 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM super.init() // Observe selection changes from featured image view model - featuredImageViewModel?.$selection.dropFirst().sink { [weak self] media in - self?.settings.featuredImageID = media?.mediaID?.intValue - }.store(in: &cancellables) + featuredImageViewModel?.$selection.dropFirst() + .sink { [weak self] media in + self?.settings.featuredImageID = media?.mediaID?.intValue + } + .store(in: &cancellables) // Initialize all cached properties refreshDisplayedCategories() @@ -224,12 +227,11 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM } func shouldShow(_ row: PostSettingsRow) -> Bool { - // FIXME: meta support missing in AnyPostWithEditContext switch row { case .jetpackAccessLevel: - return blog.supports(.wpComRESTAPI) + return blog.supports(.jetpackNewsletter) case .jetpackNewsletterEmailOptions: - return blog.supports(.wpComRESTAPI) && context == .publishing + return blog.supports(.jetpackNewsletter) && context == .publishing } } @@ -254,20 +256,26 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM self.track(.intelligenceSuggestedTagsGenerated, properties: ["count": tags.count]) } catch { guard let self else { return } - self.track(.intelligenceGenerationFailed, properties: ["description": (error as NSError).debugDescription]) + self.track( + .intelligenceGenerationFailed, + properties: ["description": (error as NSError).debugDescription] + ) } } - cancellables.insert(AnyCancellable { - task.cancel() - }) + cancellables.insert( + AnyCancellable { + task.cancel() + } + ) } private func refreshCustomTaxonomies() { - let postType: String? = switch post { - case is Post: "post" - case is Page: "page" - default: nil - } + let postType: String? = + switch post { + case is Post: "post" + case is Page: "page" + default: nil + } guard let postType else { customTaxonomies = [] return @@ -331,8 +339,9 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM private func refreshParentPageText() { if let page = post as? Page, - let context = page.managedObjectContext, - let parentPageID = settings.parentPageID { + let context = page.managedObjectContext, + let parentPageID = settings.parentPageID + { parentPageText = Page.parentPageText(in: context, parentID: NSNumber(value: parentPageID)) } else { parentPageText = nil @@ -348,7 +357,8 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM func buttonSaveTapped() { // Check if the post still exists guard let context = post.managedObjectContext, - let _ = try? context.existingObject(with: post.objectID) else { + let _ = try? context.existingObject(with: post.objectID) + else { isShowingDeletedAlert = true return } @@ -400,7 +410,8 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM func buttonPublishTapped() { // Check if the post still exists guard let context = post.managedObjectContext, - let _ = try? context.existingObject(with: post.objectID) else { + let _ = try? context.existingObject(with: post.objectID) + else { isShowingDeletedAlert = true return } @@ -490,8 +501,9 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM private var isSocialConnectionSetupDismissed: Bool { get { guard let blogID = blog.dotComID?.intValue, - let dictionary = preferences.dictionary(forKey: Constants.noConnectionKey) as? [String: Bool], - let value = dictionary["\(blogID)"] else { + let dictionary = preferences.dictionary(forKey: Constants.noConnectionKey) as? [String: Bool], + let value = dictionary["\(blogID)"] + else { return false } return value @@ -534,7 +546,8 @@ final class PostSettingsViewModel: NSObject, ObservableObject, PostSettingsViewM func showSocialSharingOptions() { guard let blogID = blog.dotComID?.intValue, - let settings = settings.sharing else { + let settings = settings.sharing + else { return wpAssertionFailure("invalid context") } let optionsVC = PrepublishingSocialAccountsViewController(