diff --git a/Modules/Sources/Storage/Model/Copiable/Models+Copiable.generated.swift b/Modules/Sources/Storage/Model/Copiable/Models+Copiable.generated.swift index 5bf17686464..2edf4db1fb2 100644 --- a/Modules/Sources/Storage/Model/Copiable/Models+Copiable.generated.swift +++ b/Modules/Sources/Storage/Model/Copiable/Models+Copiable.generated.swift @@ -64,7 +64,8 @@ extension Storage.GeneralAppSettings { featureAnnouncementCampaignSettings: CopiableProp<[FeatureAnnouncementCampaign: FeatureAnnouncementCampaignSettings]> = .copy, sitesWithAtLeastOneIPPTransactionFinished: CopiableProp> = .copy, isEUShippingNoticeDismissed: CopiableProp = .copy, - isCustomFieldsTopBannerDismissed: CopiableProp = .copy + isCustomFieldsTopBannerDismissed: CopiableProp = .copy, + isPOSSurveyNotificationScheduled: CopiableProp = .copy ) -> Storage.GeneralAppSettings { let installationDate = installationDate ?? self.installationDate let feedbacks = feedbacks ?? self.feedbacks @@ -77,6 +78,7 @@ extension Storage.GeneralAppSettings { let sitesWithAtLeastOneIPPTransactionFinished = sitesWithAtLeastOneIPPTransactionFinished ?? self.sitesWithAtLeastOneIPPTransactionFinished let isEUShippingNoticeDismissed = isEUShippingNoticeDismissed ?? self.isEUShippingNoticeDismissed let isCustomFieldsTopBannerDismissed = isCustomFieldsTopBannerDismissed ?? self.isCustomFieldsTopBannerDismissed + let isPOSSurveyNotificationScheduled = isPOSSurveyNotificationScheduled ?? self.isPOSSurveyNotificationScheduled return Storage.GeneralAppSettings( installationDate: installationDate, @@ -89,7 +91,8 @@ extension Storage.GeneralAppSettings { featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings, sitesWithAtLeastOneIPPTransactionFinished: sitesWithAtLeastOneIPPTransactionFinished, isEUShippingNoticeDismissed: isEUShippingNoticeDismissed, - isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed + isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed, + isPOSSurveyNotificationScheduled: isPOSSurveyNotificationScheduled ) } } diff --git a/Modules/Sources/Storage/Model/GeneralAppSettings.swift b/Modules/Sources/Storage/Model/GeneralAppSettings.swift index 09bc1f56357..fd5b240517a 100644 --- a/Modules/Sources/Storage/Model/GeneralAppSettings.swift +++ b/Modules/Sources/Storage/Model/GeneralAppSettings.swift @@ -56,6 +56,10 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable { /// public var isCustomFieldsTopBannerDismissed: Bool + /// Whether the Point of Sale survey notification has been scheduled + /// + public var isPOSSurveyNotificationScheduled: Bool + public init(installationDate: Date?, feedbacks: [FeedbackType: FeedbackSettings], isViewAddOnsSwitchEnabled: Bool, @@ -66,7 +70,8 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable { featureAnnouncementCampaignSettings: [FeatureAnnouncementCampaign: FeatureAnnouncementCampaignSettings], sitesWithAtLeastOneIPPTransactionFinished: Set, isEUShippingNoticeDismissed: Bool, - isCustomFieldsTopBannerDismissed: Bool) { + isCustomFieldsTopBannerDismissed: Bool, + isPOSSurveyNotificationScheduled: Bool) { self.installationDate = installationDate self.feedbacks = feedbacks self.isViewAddOnsSwitchEnabled = isViewAddOnsSwitchEnabled @@ -78,6 +83,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable { self.sitesWithAtLeastOneIPPTransactionFinished = sitesWithAtLeastOneIPPTransactionFinished self.isEUShippingNoticeDismissed = isEUShippingNoticeDismissed self.isCustomFieldsTopBannerDismissed = isCustomFieldsTopBannerDismissed + self.isPOSSurveyNotificationScheduled = isPOSSurveyNotificationScheduled } public static var `default`: Self { @@ -90,7 +96,8 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable { featureAnnouncementCampaignSettings: [:], sitesWithAtLeastOneIPPTransactionFinished: [], isEUShippingNoticeDismissed: false, - isCustomFieldsTopBannerDismissed: false) + isCustomFieldsTopBannerDismissed: false, + isPOSSurveyNotificationScheduled: false) } /// Returns the status of a given feedback type. If the feedback is not stored in the feedback array. it is assumed that it has a pending status. @@ -120,7 +127,8 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable { featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings, sitesWithAtLeastOneIPPTransactionFinished: sitesWithAtLeastOneIPPTransactionFinished, isEUShippingNoticeDismissed: isEUShippingNoticeDismissed, - isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed + isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed, + isPOSSurveyNotificationScheduled: isPOSSurveyNotificationScheduled ) } @@ -141,7 +149,8 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable { featureAnnouncementCampaignSettings: updatedSettings, sitesWithAtLeastOneIPPTransactionFinished: sitesWithAtLeastOneIPPTransactionFinished, isEUShippingNoticeDismissed: isEUShippingNoticeDismissed, - isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed + isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed, + isPOSSurveyNotificationScheduled: isPOSSurveyNotificationScheduled ) } } @@ -167,7 +176,7 @@ extension GeneralAppSettings { forKey: .sitesWithAtLeastOneIPPTransactionFinished) ?? Set([]) self.isEUShippingNoticeDismissed = try container.decodeIfPresent(Bool.self, forKey: .isEUShippingNoticeDismissed) ?? false self.isCustomFieldsTopBannerDismissed = try container.decodeIfPresent(Bool.self, forKey: .isCustomFieldsTopBannerDismissed) ?? false - + self.isPOSSurveyNotificationScheduled = try container.decodeIfPresent(Bool.self, forKey: .isPOSSurveyNotificationScheduled) ?? false // Decode new properties with `decodeIfPresent` and provide a default value if necessary. } } diff --git a/Modules/Sources/Yosemite/Actions/AppSettingsAction.swift b/Modules/Sources/Yosemite/Actions/AppSettingsAction.swift index 4b62544136b..6057da13e87 100644 --- a/Modules/Sources/Yosemite/Actions/AppSettingsAction.swift +++ b/Modules/Sources/Yosemite/Actions/AppSettingsAction.swift @@ -366,4 +366,18 @@ public enum AppSettingsAction: Action { /// Loads Loads the state of the App Passwords Experiment feature /// case getAppPasswordsExperimentSettingState(onCompletion: (Bool) -> Void) + + // MARK: - Point of Sale Surveys + + /// Sets the POS survey notification as scheduled + /// + case setPOSSurveyNotificationScheduled(onCompletion: (Result) -> Void) + + /// Gets whether the POS survey notification has been scheduled + /// + case getPOSSurveyNotificationScheduled(onCompletion: (Bool) -> Void) + + /// Resets the POS survey notification scheduled state + /// At the moment this one is used for testing only. To remove in WOOMOB-1480 + case resetPOSSurveyNotificationScheduled(onCompletion: (Result) -> Void) } diff --git a/Modules/Sources/Yosemite/Stores/AppSettingsStore.swift b/Modules/Sources/Yosemite/Stores/AppSettingsStore.swift index 10963d12b5f..3ea61946208 100644 --- a/Modules/Sources/Yosemite/Stores/AppSettingsStore.swift +++ b/Modules/Sources/Yosemite/Stores/AppSettingsStore.swift @@ -293,6 +293,12 @@ public class AppSettingsStore: Store { setAppPasswordsExperimentSettingEnabled(isOn: value, onCompletion: onCompletion) case .getAppPasswordsExperimentSettingState(let onCompletion): getAppPasswordsExperimentSettingEnabled(onCompletion: onCompletion) + case .setPOSSurveyNotificationScheduled(onCompletion: let onCompletion): + setPOSSurveyNotificationScheduled(onCompletion: onCompletion) + case .getPOSSurveyNotificationScheduled(onCompletion: let onCompletion): + getPOSSurveyNotificationScheduled(onCompletion: onCompletion) + case .resetPOSSurveyNotificationScheduled(onCompletion: let onCompletion): + resetPOSSurveyNotificationScheduled(onCompletion: onCompletion) } } } @@ -1290,6 +1296,32 @@ private extension AppSettingsStore { } } +// MARK: - Point of Sale surveys +// +private extension AppSettingsStore { + func setPOSSurveyNotificationScheduled(onCompletion: (Result) -> Void) { + do { + try generalAppSettings.setValue(true, for: \.isPOSSurveyNotificationScheduled) + onCompletion(.success(())) + } catch { + onCompletion(.failure(error)) + } + } + + func getPOSSurveyNotificationScheduled(onCompletion: (Bool) -> Void) { + onCompletion(generalAppSettings.value(for: \.isPOSSurveyNotificationScheduled)) + } + + func resetPOSSurveyNotificationScheduled(onCompletion: (Result) -> Void) { + do { + try generalAppSettings.setValue(false, for: \.isPOSSurveyNotificationScheduled) + onCompletion(.success(())) + } catch { + onCompletion(.failure(error)) + } + } +} + // MARK: - Errors /// Errors diff --git a/Modules/Tests/StorageTests/Model/AppSettings/GeneralAppSettingsTests.swift b/Modules/Tests/StorageTests/Model/AppSettings/GeneralAppSettingsTests.swift index a19ab4b1233..c3da0eca5f8 100644 --- a/Modules/Tests/StorageTests/Model/AppSettings/GeneralAppSettingsTests.swift +++ b/Modules/Tests/StorageTests/Model/AppSettings/GeneralAppSettingsTests.swift @@ -51,6 +51,14 @@ final class GeneralAppSettingsTests: XCTestCase { XCTAssertEqual(newSettings.feedbacks[.general], newFeedback) } + func test_isPOSSurveyNotificationScheduled_defaults_to_false() { + // Given + let settings = createGeneralAppSettings() + + // Then + XCTAssertFalse(settings.isPOSSurveyNotificationScheduled) + } + func test_updating_properties_to_generalAppSettings_does_not_breaks_decoding() throws { // Given let installationDate = Date(timeIntervalSince1970: 1630314000) // Mon Aug 30 2021 09:00:00 UTC+0000 @@ -63,6 +71,7 @@ final class GeneralAppSettingsTests: XCTestCase { FeatureAnnouncementCampaignSettings(dismissedDate: Date(), remindAfter: nil)] let sitesWithAtLeastOneIPPTransactionFinished: Set = [1234, 123, 12, 1] let isCustomFieldsTopBannerDismissed = true + let isPOSSurveyNotificationScheduled = true let previousSettings = GeneralAppSettings(installationDate: installationDate, feedbacks: feedbackSettings, isViewAddOnsSwitchEnabled: true, @@ -73,13 +82,15 @@ final class GeneralAppSettingsTests: XCTestCase { featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings, sitesWithAtLeastOneIPPTransactionFinished: sitesWithAtLeastOneIPPTransactionFinished, isEUShippingNoticeDismissed: false, - isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed) + isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed, + isPOSSurveyNotificationScheduled: isPOSSurveyNotificationScheduled) let previousEncodedSettings = try JSONEncoder().encode(previousSettings) var previousSettingsJson = try JSONSerialization.jsonObject(with: previousEncodedSettings, options: .allowFragments) as? [String: Any] // When previousSettingsJson?.removeValue(forKey: "isViewAddOnsSwitchEnabled") + previousSettingsJson?.removeValue(forKey: "isPOSSurveyNotificationScheduled") let newEncodedSettings = try JSONSerialization.data(withJSONObject: previousSettingsJson as Any, options: .fragmentsAllowed) let newSettings = try JSONDecoder().decode(GeneralAppSettings.self, from: newEncodedSettings) @@ -93,6 +104,7 @@ final class GeneralAppSettingsTests: XCTestCase { assertEqual(newSettings.featureAnnouncementCampaignSettings, featureAnnouncementCampaignSettings) assertEqual(newSettings.sitesWithAtLeastOneIPPTransactionFinished, sitesWithAtLeastOneIPPTransactionFinished) assertEqual(newSettings.isCustomFieldsTopBannerDismissed, isCustomFieldsTopBannerDismissed) + assertEqual(newSettings.isPOSSurveyNotificationScheduled, false) } } @@ -122,6 +134,7 @@ private extension GeneralAppSettingsTests { featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings, sitesWithAtLeastOneIPPTransactionFinished: sitesWithAtLeastOneIPPTransactionFinished, isEUShippingNoticeDismissed: isEUShippingNoticeDismissed, - isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed) + isCustomFieldsTopBannerDismissed: isCustomFieldsTopBannerDismissed, + isPOSSurveyNotificationScheduled: false) } } diff --git a/Modules/Tests/YosemiteTests/Stores/AppSettings/InAppFeedbackCardVisibilityUseCaseTests.swift b/Modules/Tests/YosemiteTests/Stores/AppSettings/InAppFeedbackCardVisibilityUseCaseTests.swift index 96cce9ca379..d627a6e346a 100644 --- a/Modules/Tests/YosemiteTests/Stores/AppSettings/InAppFeedbackCardVisibilityUseCaseTests.swift +++ b/Modules/Tests/YosemiteTests/Stores/AppSettings/InAppFeedbackCardVisibilityUseCaseTests.swift @@ -244,7 +244,8 @@ private extension InAppFeedbackCardVisibilityUseCaseTests { featureAnnouncementCampaignSettings: [:], sitesWithAtLeastOneIPPTransactionFinished: [], isEUShippingNoticeDismissed: false, - isCustomFieldsTopBannerDismissed: false) + isCustomFieldsTopBannerDismissed: false, + isPOSSurveyNotificationScheduled: false) return settings } } diff --git a/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests.swift b/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests.swift index 93c9bd9ec9a..5761d1dbf80 100644 --- a/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests.swift +++ b/Modules/Tests/YosemiteTests/Stores/AppSettingsStoreTests.swift @@ -1506,6 +1506,96 @@ extension AppSettingsStoreTests { // Then XCTAssertNil(loadedOrderStatus) } + + // MARK: - Point of Sale Survey Notification + + func test_getPOSSurveyNotificationScheduled_returns_false_on_new_generalAppSettings() throws { + // Given + try fileStorage?.deleteFile(at: expectedGeneralAppSettingsFileURL) + + // When + let result: Bool = waitFor { promise in + let action = AppSettingsAction.getPOSSurveyNotificationScheduled { isScheduled in + promise(isScheduled) + } + self.subject?.onAction(action) + } + + // Then + XCTAssertFalse(result) + } + + func test_getPOSSurveyNotificationScheduled_returns_true_after_setting_as_scheduled() throws { + // Given + try fileStorage?.deleteFile(at: expectedGeneralAppSettingsFileURL) + let setAction = AppSettingsAction.setPOSSurveyNotificationScheduled { _ in } + subject?.onAction(setAction) + + // When + let result: Bool = waitFor { promise in + let action = AppSettingsAction.getPOSSurveyNotificationScheduled { isScheduled in + promise(isScheduled) + } + self.subject?.onAction(action) + } + + // Then + XCTAssertTrue(result) + } + + func test_setPOSSurveyNotificationScheduled_stores_value_correctly() throws { + // Given + try fileStorage?.deleteFile(at: expectedGeneralAppSettingsFileURL) + + // When + var result: Result? + let action = AppSettingsAction.setPOSSurveyNotificationScheduled { aResult in + result = aResult + } + subject?.onAction(action) + + // Then + XCTAssertTrue(try XCTUnwrap(result).isSuccess) + + let savedSettings: GeneralAppSettings = try XCTUnwrap(fileStorage?.data(for: expectedGeneralAppSettingsFileURL)) + XCTAssertTrue(savedSettings.isPOSSurveyNotificationScheduled) + } + + func test_resetPOSSurveyNotificationScheduled_resets_to_false_after_being_set_to_true() throws { + // Given + try fileStorage?.deleteFile(at: expectedGeneralAppSettingsFileURL) + + // 1. Set to true + let setAction = AppSettingsAction.setPOSSurveyNotificationScheduled { _ in } + subject?.onAction(setAction) + + // 2. Verify it's true + let checkBeforeReset: Bool = waitFor { promise in + let action = AppSettingsAction.getPOSSurveyNotificationScheduled { isScheduled in + promise(isScheduled) + } + self.subject?.onAction(action) + } + XCTAssertTrue(checkBeforeReset) + + // When - 3. Reset it + var resetResult: Result? + let resetAction = AppSettingsAction.resetPOSSurveyNotificationScheduled { aResult in + resetResult = aResult + } + subject?.onAction(resetAction) + + // Then - 4. Verify it's false + XCTAssertTrue(try XCTUnwrap(resetResult).isSuccess) + + let checkAfterReset: Bool = waitFor { promise in + let action = AppSettingsAction.getPOSSurveyNotificationScheduled { isScheduled in + promise(isScheduled) + } + self.subject?.onAction(action) + } + XCTAssertFalse(checkAfterReset) + } } // MARK: - Utils @@ -1527,7 +1617,8 @@ private extension AppSettingsStoreTests { featureAnnouncementCampaignSettings: [:], sitesWithAtLeastOneIPPTransactionFinished: [], isEUShippingNoticeDismissed: false, - isCustomFieldsTopBannerDismissed: false + isCustomFieldsTopBannerDismissed: false, + isPOSSurveyNotificationScheduled: false ) return (settings, feedback) } @@ -1542,7 +1633,8 @@ private extension AppSettingsStoreTests { featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings, sitesWithAtLeastOneIPPTransactionFinished: [], isEUShippingNoticeDismissed: false, - isCustomFieldsTopBannerDismissed: false + isCustomFieldsTopBannerDismissed: false, + isPOSSurveyNotificationScheduled: false ) return settings }