Skip to content

Commit c487172

Browse files
authored
Merge pull request #8657 from woocommerce/issue/8589-ipp-banner-dismiss
[IPPInAppFeedback] Banner dismiss logic
2 parents 084f7d9 + 914d62c commit c487172

File tree

8 files changed

+149
-27
lines changed

8 files changed

+149
-27
lines changed

Storage/Storage/Model/Feature Announcements/FeatureAnnouncementCampaign.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ public enum FeatureAnnouncementCampaign: String, Codable, Equatable {
44
case upsellCardReaders = "upsell_card_readers"
55
case linkedProductsPromo = "linked_products_promo"
66
case productsOnboarding = "products_onboarding_first_product"
7+
case IPP = "IPP_feedback_request"
78

89
/// Added for use in `test_setFeatureAnnouncementDismissed_with_another_campaign_previously_dismissed_keeps_values_for_both`
910
/// This can be removed when we have a second campaign, which can be used in the above test instead.

WooCommerce/Classes/ViewModels/Feature Announcement Cards/FeatureAnnouncementCardViewModel.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,9 @@ class FeatureAnnouncementCardViewModel: AnnouncementCardViewModelProtocol {
106106
}
107107

108108
private func storeDismissedSetting(remindLater: Bool) {
109+
let remindAfterDays = remindLater ? 0 : nil
109110
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: config.campaign,
110-
remindLater: remindLater,
111+
remindAfterDays: remindAfterDays,
111112
onCompletion: nil)
112113
stores.dispatch(action)
113114
shouldBeVisible = false

WooCommerce/Classes/ViewModels/Feature Announcement Cards/ProductsOnboardingAnnouncementCardViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ struct ProductsOnboardingAnnouncementCardViewModel: AnnouncementCardViewModelPro
3434
///
3535
func dontShowAgainTapped() {
3636
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .productsOnboarding,
37-
remindLater: false,
37+
remindAfterDays: nil,
3838
onCompletion: nil)
3939
ServiceLocator.stores.dispatch(action)
4040
}

WooCommerce/Classes/ViewRelated/Orders/OrderListViewController.swift

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -802,8 +802,10 @@ private extension OrderListViewController {
802802
}
803803

804804
private func createIPPFeedbackTopBanner(survey: SurveyViewController.Source) -> TopBannerView {
805-
let shareIPPFeedbackAction = TopBannerViewModel.ActionButton(title: Localization.shareFeedbackButton, action: { _ in
806-
self.displayIPPFeedbackBannerSurvey(survey: survey)
805+
let shareIPPFeedbackAction = TopBannerViewModel.ActionButton(title: Localization.shareFeedbackButton, action: { [weak self] _ in
806+
self?.displayIPPFeedbackBannerSurvey(survey: survey)
807+
// We dismiss the banner at this point as we cannot know if the user successfully submitted it
808+
self?.viewModel.IPPFeedbackBannerWasDismissed()
807809
})
808810

809811
var bannerTitle = ""
@@ -828,7 +830,9 @@ private extension OrderListViewController {
828830
infoText: bannerText,
829831
icon: UIImage.gridicon(.comment),
830832
isExpanded: true,
831-
topButton: .dismiss(handler: { }),
833+
topButton: .dismiss(handler: {
834+
self.showIPPFeedbackDismissAlert()
835+
}),
832836
actionButtons: [shareIPPFeedbackAction]
833837
)
834838
let topBannerView = TopBannerView(viewModel: viewModel)
@@ -840,6 +844,26 @@ private extension OrderListViewController {
840844
let surveyNavigation = SurveyCoordinatingController(survey: survey)
841845
self.present(surveyNavigation, animated: true, completion: nil)
842846
}
847+
848+
private func showIPPFeedbackDismissAlert() {
849+
let actionSheet = UIAlertController(
850+
title: Localization.dismissTitle,
851+
message: Localization.dismissMessage,
852+
preferredStyle: .alert
853+
)
854+
855+
let remindMeLaterAction = UIAlertAction( title: Localization.remindMeLater, style: .default) { [weak self] _ in
856+
self?.viewModel.IPPFeedbackBannerRemindMeLaterTapped()
857+
}
858+
actionSheet.addAction(remindMeLaterAction)
859+
860+
let dontShowAgainAction = UIAlertAction( title: Localization.dontShowAgain, style: .default) { [weak self] _ in
861+
self?.viewModel.IPPFeedbackBannerDontShowAgainTapped()
862+
}
863+
actionSheet.addAction(dontShowAgainAction)
864+
865+
self.present(actionSheet, animated: true)
866+
}
843867
}
844868

845869
// MARK: - Constants
@@ -886,6 +910,21 @@ private extension OrderListViewController {
886910
comment: "Title of the feedback action button on the In-Person Payments feedback banner"
887911
)
888912

913+
static let dismissTitle = NSLocalizedString("Give feedback",
914+
comment: "Title of the modal confirmation screen when the In-Person Payments feedback banner is dismissed"
915+
)
916+
917+
static let dismissMessage = NSLocalizedString("No worries! You can always go to Settings in the Menu to send us feedback.",
918+
comment: "Message of the modal confirmation screen when the In-Person Payments feedback banner is dismissed")
919+
920+
static let remindMeLater = NSLocalizedString("Remind me later",
921+
comment: "Title of the button shown when the In-Person Payments feedback banner is dismissed."
922+
)
923+
924+
static let dontShowAgain = NSLocalizedString("Don't show again",
925+
comment: "Title of the button shown when the In-Person Payments feedback banner is dismissed."
926+
)
927+
889928
static func markCompletedNoticeTitle(orderID: Int64) -> String {
890929
let format = NSLocalizedString(
891930
"Order #%1$d marked as completed",

WooCommerce/Classes/ViewRelated/Orders/OrderListViewModel.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,38 @@ final class OrderListViewModel {
346346
}
347347
}
348348

349+
// MARK: - In-Person Payments Feedback Banner
350+
351+
extension OrderListViewModel {
352+
func dismissIPPFeedbackBanner(remindAfterDays: Int?) {
353+
// Updates the IPP feedback banner status as dismissed
354+
let updateFeedbackStatus = AppSettingsAction.updateFeedbackStatus(type: .IPP, status: .dismissed) { [weak self] _ in
355+
self?.hideIPPFeedbackBanner = true
356+
}
357+
stores.dispatch(updateFeedbackStatus)
358+
359+
// Updates the IPP feedback banner status to be reminded later, or never
360+
let updateBannerVisibility = AppSettingsAction.setFeatureAnnouncementDismissed(
361+
campaign: .IPP,
362+
remindAfterDays: remindAfterDays,
363+
onCompletion: nil
364+
)
365+
stores.dispatch(updateBannerVisibility)
366+
}
367+
368+
func IPPFeedbackBannerRemindMeLaterTapped() {
369+
dismissIPPFeedbackBanner(remindAfterDays: Constants.remindIPPBannerDismissalAfterDays)
370+
}
371+
372+
func IPPFeedbackBannerDontShowAgainTapped() {
373+
dismissIPPFeedbackBanner(remindAfterDays: nil)
374+
}
375+
376+
func IPPFeedbackBannerWasDismissed() {
377+
dismissIPPFeedbackBanner(remindAfterDays: nil)
378+
}
379+
}
380+
349381
// MARK: - Remote Notifications Observation
350382

351383
private extension OrderListViewModel {
@@ -461,5 +493,6 @@ private extension OrderListViewModel {
461493
static let paymentMethodTitle = "WooCommerce In-Person Payments"
462494
static let receiptURLKey = "receipt_url"
463495
static let numberOfTransactions = 10
496+
static let remindIPPBannerDismissalAfterDays = 7
464497
}
465498
}

Yosemite/Yosemite/Actions/AppSettingsAction.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ public enum AppSettingsAction: Action {
197197

198198
// MARK: - Feature Announcement Card Visibility
199199

200-
case setFeatureAnnouncementDismissed(campaign: FeatureAnnouncementCampaign, remindLater: Bool, onCompletion: ((Result<Bool, Error>) -> ())?)
200+
case setFeatureAnnouncementDismissed(
201+
campaign: FeatureAnnouncementCampaign,
202+
remindAfterDays: Int?,
203+
onCompletion: ((Result<Bool, Error>) -> ())?
204+
)
201205

202206
case getFeatureAnnouncementVisibility(campaign: FeatureAnnouncementCampaign, onCompletion: (Result<Bool, Error>) -> ())
203207

Yosemite/Yosemite/Stores/AppSettingsStore.swift

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ public class AppSettingsStore: Store {
178178
setCouponManagementFeatureSwitchState(isEnabled: isEnabled, onCompletion: onCompletion)
179179
case .loadCouponManagementFeatureSwitchState(let onCompletion):
180180
loadCouponManagementFeatureSwitchState(onCompletion: onCompletion)
181-
case .setFeatureAnnouncementDismissed(campaign: let campaign, remindLater: let remindLater, onCompletion: let completion):
182-
setFeatureAnnouncementDismissed(campaign: campaign, remindLater: remindLater, onCompletion: completion)
181+
case .setFeatureAnnouncementDismissed(campaign: let campaign, remindAfterDays: let remindAfterDays, onCompletion: let completion):
182+
setFeatureAnnouncementDismissed(campaign: campaign, remindAfterDays: remindAfterDays, onCompletion: completion)
183183
case .getFeatureAnnouncementVisibility(campaign: let campaign, onCompletion: let completion):
184184
getFeatureAnnouncementVisibility(campaign: campaign, onCompletion: completion)
185185
case .setSkippedCashOnDeliveryOnboardingStep(siteID: let siteID):
@@ -758,20 +758,26 @@ private extension AppSettingsStore {
758758

759759
extension AppSettingsStore {
760760

761-
func setFeatureAnnouncementDismissed(campaign: FeatureAnnouncementCampaign, remindLater: Bool, onCompletion: ((Result<Bool, Error>) -> ())?) {
762-
do {
763-
let remindAfter = remindLater ? Date().addingDays(14) : nil
764-
let newSettings = FeatureAnnouncementCampaignSettings(dismissedDate: Date(), remindAfter: remindAfter)
765-
766-
let settings = generalAppSettings.settings
767-
let settingsToSave = settings.replacing(featureAnnouncementSettings: newSettings, for: campaign)
768-
try generalAppSettings.saveSettings(settingsToSave)
769-
770-
onCompletion?(.success(true))
771-
} catch {
772-
onCompletion?(.failure(error))
761+
func setFeatureAnnouncementDismissed(
762+
campaign: FeatureAnnouncementCampaign,
763+
remindAfterDays: Int?,
764+
onCompletion: ((Result<Bool, Error>) -> ())?) {
765+
do {
766+
guard let remindAfterDays else {
767+
return
768+
}
769+
let remindAfter = Date().addingDays(remindAfterDays)
770+
let newSettings = FeatureAnnouncementCampaignSettings(dismissedDate: Date(), remindAfter: remindAfter)
771+
772+
let settings = generalAppSettings.settings
773+
let settingsToSave = settings.replacing(featureAnnouncementSettings: newSettings, for: campaign)
774+
try generalAppSettings.saveSettings(settingsToSave)
775+
776+
onCompletion?(.success(true))
777+
} catch {
778+
onCompletion?(.failure(error))
779+
}
773780
}
774-
}
775781

776782
func getFeatureAnnouncementVisibility(campaign: FeatureAnnouncementCampaign, onCompletion: (Result<Bool, Error>) -> ()) {
777783
guard let campaignSettings = generalAppSettings.value(for: \.featureAnnouncementCampaignSettings)[campaign] else {

Yosemite/YosemiteTests/Stores/AppSettingsStoreTests.swift

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -841,14 +841,31 @@ final class AppSettingsStoreTests: XCTestCase {
841841

842842
extension AppSettingsStoreTests {
843843

844+
func test_setFeatureAnnouncementDismissed_for_campaign_when_remindAfterDays_is_nil_then_does_not_store_current_date() throws {
845+
// Given
846+
try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL)
847+
// When
848+
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindAfterDays: nil, onCompletion: nil)
849+
subject?.onAction(action)
850+
851+
// Then
852+
var savedSettings: GeneralAppSettings? = try XCTUnwrap(fileStorage?.data(for: expectedGeneralAppSettingsFileURL))
853+
XCTAssertNil(savedSettings)
854+
guard let savedSettings else {
855+
return
856+
}
857+
var savedDate: Date? = try XCTUnwrap( savedSettings.featureAnnouncementCampaignSettings[.upsellCardReaders]?.dismissedDate)
858+
XCTAssertNil(savedDate)
859+
}
860+
844861
func test_setFeatureAnnouncementDismissed_for_campaign_stores_current_date() throws {
845862
// Given
846863
let currentTime = Date()
847864

848865
try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL)
849866

850867
// When
851-
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: false, onCompletion: nil)
868+
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindAfterDays: 0, onCompletion: nil)
852869
subject?.onAction(action)
853870

854871
// Then
@@ -859,14 +876,15 @@ extension AppSettingsStoreTests {
859876
XCTAssert(Calendar.current.isDate(actualDismissDate, inSameDayAs: currentTime))
860877
}
861878

862-
func test_setFeatureAnnouncementDismissed_with_remindLater_true_stores_reminder_date_in_two_weeks() throws {
879+
func test_setFeatureAnnouncementDismissed_when_remindAfterDays_is_two_weeks_then_stores_reminder_date_is_two_weeks() throws {
863880
// Given
864-
let twoWeeksTime = Calendar.current.date(byAdding: .day, value: 14, to: Date())!
881+
let remindAfterDays = 14
882+
let twoWeeksTime = Calendar.current.date(byAdding: .day, value: remindAfterDays, to: Date())!
865883

866884
try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL)
867885

868886
// When
869-
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: true, onCompletion: nil)
887+
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindAfterDays: remindAfterDays, onCompletion: nil)
870888
subject?.onAction(action)
871889

872890
// Then
@@ -877,18 +895,38 @@ extension AppSettingsStoreTests {
877895
XCTAssert(Calendar.current.isDate(actualRemindAfter, inSameDayAs: twoWeeksTime))
878896
}
879897

898+
func test_setFeatureAnnouncementDismissed_when_remindAfterDays_is_seven_days_stores_reminder_then_date_saved_date_is_one_week() throws {
899+
// Given
900+
let remindAfterDays = 7
901+
let oneWeekTime = Calendar.current.date(byAdding: .day, value: remindAfterDays, to: Date())!
902+
903+
try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL)
904+
905+
// When
906+
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindAfterDays: remindAfterDays, onCompletion: nil)
907+
subject?.onAction(action)
908+
909+
// Then
910+
let savedSettings: GeneralAppSettings = try XCTUnwrap(fileStorage?.data(for: expectedGeneralAppSettingsFileURL))
911+
912+
let actualRemindAfter = try XCTUnwrap( savedSettings.featureAnnouncementCampaignSettings[.upsellCardReaders]?.remindAfter)
913+
914+
XCTAssert(Calendar.current.isDate(actualRemindAfter, inSameDayAs: oneWeekTime))
915+
}
916+
880917
func test_setFeatureAnnouncementDismissed_with_another_campaign_previously_dismissed_keeps_values_for_both() throws {
881918
// Given
882919
try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL)
883920

884921
let currentTime = Date()
885-
let date = Date(timeIntervalSince1970: 100)
922+
let datePrior = Date(timeIntervalSince1970: 100)
923+
let date = Date()
886924

887925
let settings = createAppSettings(featureAnnouncementCampaignSettings: [.test: .init(dismissedDate: date, remindAfter: nil)])
888926
try fileStorage?.write(settings, to: expectedGeneralAppSettingsFileURL)
889927

890928
// When
891-
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: false, onCompletion: nil)
929+
let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindAfterDays: 0, onCompletion: nil)
892930
subject?.onAction(action)
893931

894932
// Then

0 commit comments

Comments
 (0)