-
Notifications
You must be signed in to change notification settings - Fork 121
[IPPInAppFeedback] Banner dismiss logic #8657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
a92bddd
8de3516
cf03baf
232f4bc
bcb327e
398874b
c7356c7
2aa3298
7b68aa7
523fe0a
70d9cbd
909bea5
f8a2586
80a1b1b
2177584
d6b1d0a
cdc5089
f294478
d5e02a2
6a53fd6
9884f22
914d62c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -796,14 +796,19 @@ private extension OrderListViewController { | |
| private func createIPPFeedbackTopBanner() -> TopBannerView { | ||
| let shareIPPFeedbackAction = TopBannerViewModel.ActionButton(title: Localization.shareFeedbackButton, action: { _ in | ||
| self.displayIPPFeedbackBannerSurvey() | ||
| // We dismiss the banner at this point as well, as we cannot know if was successfully submited or not at this point | ||
| // We will check the FeedbackStatus to know if we must display it again or not | ||
| self.viewModel.dismissIPPFeedbackBanner(remindLater: false, remindAfter: nil) | ||
| }) | ||
|
|
||
| let viewModel = TopBannerViewModel( | ||
| title: Localization.feedbackBannerTitle, | ||
| infoText: Localization.feedbackBannerContent, | ||
| icon: UIImage.gridicon(.comment), | ||
| isExpanded: true, | ||
| topButton: .dismiss(handler: { }), | ||
| topButton: .dismiss(handler: { | ||
| self.dismissIPPFeedbackBannerSurvey() | ||
| }), | ||
| actionButtons: [shareIPPFeedbackAction] | ||
| ) | ||
| let topBannerView = TopBannerView(viewModel: viewModel) | ||
|
|
@@ -816,6 +821,34 @@ private extension OrderListViewController { | |
| let surveyNavigation = SurveyCoordinatingController(survey: .IPPFeedback) | ||
| self.present(surveyNavigation, animated: true, completion: nil) | ||
| } | ||
|
|
||
| private func dismissIPPFeedbackBannerSurvey() { | ||
|
||
| let actionSheet = UIAlertController( | ||
| title: Localization.dismissTitle, | ||
| message: Localization.dismissMessage, | ||
| preferredStyle: .alert | ||
| ) | ||
|
|
||
| let remindMeLaterAction = UIAlertAction( title: Localization.remindMeLater, style: .default) { [weak self] _ in | ||
| self?.remindMeLaterTapped() | ||
| } | ||
| actionSheet.addAction(remindMeLaterAction) | ||
|
|
||
| let dontShowAgainAction = UIAlertAction( title: Localization.dontShowAgain, style: .default) { [weak self] _ in | ||
| self?.dontShowAgainTapped() | ||
| } | ||
| actionSheet.addAction(dontShowAgainAction) | ||
|
|
||
| self.present(actionSheet, animated: true) | ||
| } | ||
|
|
||
| private func remindMeLaterTapped() { | ||
| viewModel.dismissIPPFeedbackBanner(remindLater: true, remindAfter: Settings.remindAfterDays) | ||
|
||
| } | ||
|
|
||
| private func dontShowAgainTapped() { | ||
| viewModel.dismissIPPFeedbackBanner(remindLater: false, remindAfter: nil) | ||
| } | ||
| } | ||
|
|
||
| // MARK: - Constants | ||
|
|
@@ -846,6 +879,17 @@ private extension OrderListViewController { | |
| comment: "Title of the feedback action button on the In-Person Payments feedback banner" | ||
| ) | ||
|
|
||
| static let dismissTitle = NSLocalizedString("Give feedback", | ||
| comment: "Title of the modal confirmation screen when the In-Person Payments feedback banner is dismissed" | ||
| ) | ||
|
|
||
| static let dismissMessage = NSLocalizedString("No worries! You can always go to Settings in the Menu to send us feedback.", | ||
| comment: "Message of the modal confirmation screen when the In-Person Payments feedback banner is dismissed") | ||
|
|
||
| static let remindMeLater = NSLocalizedString("Remind me later", comment: "") | ||
|
|
||
| static let dontShowAgain = NSLocalizedString("Don't show again", comment: "") | ||
|
|
||
| static func markCompletedNoticeTitle(orderID: Int64) -> String { | ||
| let format = NSLocalizedString( | ||
| "Order #%1$d marked as completed", | ||
|
|
@@ -867,6 +911,7 @@ private extension OrderListViewController { | |
| static let estimatedHeaderHeight = CGFloat(43) | ||
| static let estimatedRowHeight = CGFloat(86) | ||
| static let placeholderRowsPerSection = [3] | ||
| static let remindAfterDays = 7 | ||
|
||
| } | ||
|
|
||
| enum State { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -196,6 +196,23 @@ final class OrderListViewModel { | |
| stores.dispatch(action) | ||
| } | ||
|
|
||
| func dismissIPPFeedbackBanner(remindLater: Bool, remindAfter: Int?) { | ||
|
||
| // Updates the IPP feedback banner status as dismissed | ||
| let updateFeedbackStatus = AppSettingsAction.updateFeedbackStatus(type: .IPP, status: .dismissed, onCompletion: { _ in | ||
| self.hideIPPFeedbackBanner = true | ||
| }) | ||
| stores.dispatch(updateFeedbackStatus) | ||
|
|
||
| // Updates the IPP feedback banner status to be reminded later, or never | ||
| let updateBannerVisibility = AppSettingsAction.setFeatureAnnouncementDismissed( | ||
| campaign: .IPP, | ||
| remindLater: remindLater, | ||
| remindAfter: remindAfter, | ||
| onCompletion: nil | ||
| ) | ||
| stores.dispatch(updateBannerVisibility) | ||
| } | ||
|
|
||
| /// Starts the snapshotsProvider, logging any errors. | ||
| private func startReceivingSnapshots() { | ||
| do { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,8 +178,8 @@ public class AppSettingsStore: Store { | |
| setCouponManagementFeatureSwitchState(isEnabled: isEnabled, onCompletion: onCompletion) | ||
| case .loadCouponManagementFeatureSwitchState(let onCompletion): | ||
| loadCouponManagementFeatureSwitchState(onCompletion: onCompletion) | ||
| case .setFeatureAnnouncementDismissed(campaign: let campaign, remindLater: let remindLater, onCompletion: let completion): | ||
| setFeatureAnnouncementDismissed(campaign: campaign, remindLater: remindLater, onCompletion: completion) | ||
| case .setFeatureAnnouncementDismissed(campaign: let campaign, remindLater: let remindLater, remindAfter: let remindAfter, onCompletion: let completion): | ||
|
||
| setFeatureAnnouncementDismissed(campaign: campaign, remindLater: remindLater, remindAfter: remindAfter, onCompletion: completion) | ||
| case .getFeatureAnnouncementVisibility(campaign: let campaign, onCompletion: let completion): | ||
| getFeatureAnnouncementVisibility(campaign: campaign, onCompletion: completion) | ||
| case .setSkippedCashOnDeliveryOnboardingStep(siteID: let siteID): | ||
|
|
@@ -758,20 +758,24 @@ private extension AppSettingsStore { | |
|
|
||
| extension AppSettingsStore { | ||
|
|
||
| func setFeatureAnnouncementDismissed(campaign: FeatureAnnouncementCampaign, remindLater: Bool, onCompletion: ((Result<Bool, Error>) -> ())?) { | ||
| do { | ||
| let remindAfter = remindLater ? Date().addingDays(14) : nil | ||
| let newSettings = FeatureAnnouncementCampaignSettings(dismissedDate: Date(), remindAfter: remindAfter) | ||
|
|
||
| let settings = generalAppSettings.settings | ||
| let settingsToSave = settings.replacing(featureAnnouncementSettings: newSettings, for: campaign) | ||
| try generalAppSettings.saveSettings(settingsToSave) | ||
|
|
||
| onCompletion?(.success(true)) | ||
| } catch { | ||
| onCompletion?(.failure(error)) | ||
| func setFeatureAnnouncementDismissed( | ||
| campaign: FeatureAnnouncementCampaign, | ||
| remindLater: Bool, | ||
| remindAfter: Int? = 14, | ||
| onCompletion: ((Result<Bool, Error>) -> ())?) { | ||
| do { | ||
| let remindAfter = remindLater ? Date().addingDays(remindAfter ?? 14) : nil | ||
| let newSettings = FeatureAnnouncementCampaignSettings(dismissedDate: Date(), remindAfter: remindAfter) | ||
|
|
||
| let settings = generalAppSettings.settings | ||
| let settingsToSave = settings.replacing(featureAnnouncementSettings: newSettings, for: campaign) | ||
| try generalAppSettings.saveSettings(settingsToSave) | ||
|
|
||
| onCompletion?(.success(true)) | ||
| } catch { | ||
| onCompletion?(.failure(error)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func getFeatureAnnouncementVisibility(campaign: FeatureAnnouncementCampaign, onCompletion: (Result<Bool, Error>) -> ()) { | ||
| guard let campaignSettings = generalAppSettings.value(for: \.featureAnnouncementCampaignSettings)[campaign] else { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -848,7 +848,7 @@ extension AppSettingsStoreTests { | |
| try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL) | ||
|
|
||
| // When | ||
| let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: false, onCompletion: nil) | ||
| let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: false, remindAfter: nil, onCompletion: nil) | ||
| subject?.onAction(action) | ||
|
|
||
| // Then | ||
|
|
@@ -866,7 +866,7 @@ extension AppSettingsStoreTests { | |
| try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL) | ||
|
|
||
| // When | ||
| let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: true, onCompletion: nil) | ||
| let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: true, remindAfter: nil, onCompletion: nil) | ||
| subject?.onAction(action) | ||
|
|
||
| // Then | ||
|
|
@@ -877,6 +877,24 @@ extension AppSettingsStoreTests { | |
| XCTAssert(Calendar.current.isDate(actualRemindAfter, inSameDayAs: twoWeeksTime)) | ||
| } | ||
|
|
||
| func test_setFeatureAnnouncementDismissed_with_remindAfter_seven_days_stores_reminder_date_in_one_week() throws { | ||
|
||
| // Given | ||
| let oneWeekTime = Calendar.current.date(byAdding: .day, value: 7, to: Date())! | ||
|
|
||
| try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL) | ||
|
|
||
| // When | ||
| let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: true, remindAfter: 7, onCompletion: nil) | ||
| subject?.onAction(action) | ||
|
|
||
| // Then | ||
| let savedSettings: GeneralAppSettings = try XCTUnwrap(fileStorage?.data(for: expectedGeneralAppSettingsFileURL)) | ||
|
|
||
| let actualRemindAfter = try XCTUnwrap( savedSettings.featureAnnouncementCampaignSettings[.upsellCardReaders]?.remindAfter) | ||
|
|
||
| XCTAssert(Calendar.current.isDate(actualRemindAfter, inSameDayAs: oneWeekTime)) | ||
| } | ||
|
|
||
| func test_setFeatureAnnouncementDismissed_with_another_campaign_previously_dismissed_keeps_values_for_both() throws { | ||
| // Given | ||
| try fileStorage?.deleteFile(at: expectedGeneralStoreSettingsFileURL) | ||
|
|
@@ -888,7 +906,7 @@ extension AppSettingsStoreTests { | |
| try fileStorage?.write(settings, to: expectedGeneralAppSettingsFileURL) | ||
|
|
||
| // When | ||
| let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: false, onCompletion: nil) | ||
| let action = AppSettingsAction.setFeatureAnnouncementDismissed(campaign: .upsellCardReaders, remindLater: false, remindAfter: nil, onCompletion: nil) | ||
| subject?.onAction(action) | ||
|
|
||
| // Then | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to add
[weak self]for these references? Also for otherselfusage within a block in this PRThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We definitely do 🙇 , added both on 70d9cbd