Skip to content

Commit 850f506

Browse files
authored
Merge pull request #8001 from woocommerce/issue/7854-delete-jitm
[Just In Time Messages] Dismiss JITM
2 parents 03c3c20 + bbbbcae commit 850f506

File tree

12 files changed

+263
-19
lines changed

12 files changed

+263
-19
lines changed

Networking/Networking.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@
102102
0359EA2127AAE58C0048DE2D /* wcpay-charge-card-present.json in Resources */ = {isa = PBXBuildFile; fileRef = 0359EA2027AAE58C0048DE2D /* wcpay-charge-card-present.json */; };
103103
0359EA2527AAF7D60048DE2D /* wcpay-charge-card.json in Resources */ = {isa = PBXBuildFile; fileRef = 0359EA2427AAF7D60048DE2D /* wcpay-charge-card.json */; };
104104
0359EA2927AC2AAD0048DE2D /* wcpay-charge-error.json in Resources */ = {isa = PBXBuildFile; fileRef = 0359EA2827AC2AAD0048DE2D /* wcpay-charge-error.json */; };
105+
035BA3AA29113CBD0056F0AD /* DataBoolMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035BA3A929113CBD0056F0AD /* DataBoolMapper.swift */; };
105106
036563DB2906938600D84BFD /* JustInTimeMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036563DA2906938600D84BFD /* JustInTimeMessage.swift */; };
106107
036563DD29069BE400D84BFD /* JustInTimeMessageListMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036563DC29069BE400D84BFD /* JustInTimeMessageListMapper.swift */; };
107108
036563DF29069C8F00D84BFD /* JustInTimeMessageListMapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 036563DE29069C8F00D84BFD /* JustInTimeMessageListMapperTests.swift */; };
@@ -835,6 +836,7 @@
835836
0359EA2027AAE58C0048DE2D /* wcpay-charge-card-present.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-charge-card-present.json"; sourceTree = "<group>"; };
836837
0359EA2427AAF7D60048DE2D /* wcpay-charge-card.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-charge-card.json"; sourceTree = "<group>"; };
837838
0359EA2827AC2AAD0048DE2D /* wcpay-charge-error.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "wcpay-charge-error.json"; sourceTree = "<group>"; };
839+
035BA3A929113CBD0056F0AD /* DataBoolMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBoolMapper.swift; sourceTree = "<group>"; };
838840
036563DA2906938600D84BFD /* JustInTimeMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustInTimeMessage.swift; sourceTree = "<group>"; };
839841
036563DC29069BE400D84BFD /* JustInTimeMessageListMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustInTimeMessageListMapper.swift; sourceTree = "<group>"; };
840842
036563DE29069C8F00D84BFD /* JustInTimeMessageListMapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JustInTimeMessageListMapperTests.swift; sourceTree = "<group>"; };
@@ -2219,6 +2221,7 @@
22192221
03DCB785262739D200C8953D /* CouponMapper.swift */,
22202222
DE2095BE279583A100171F1C /* CouponReportListMapper.swift */,
22212223
45150A9D26836A57006922EA /* CountryListMapper.swift */,
2224+
035BA3A929113CBD0056F0AD /* DataBoolMapper.swift */,
22222225
B524193E21AC5FE400D6FC0A /* DotcomDeviceMapper.swift */,
22232226
2676F4CD290AE6BB00C7A15B /* EntityIDMapper.swift */,
22242227
24F98C572502EA8800F49B68 /* FeatureFlagMapper.swift */,
@@ -3161,6 +3164,7 @@
31613164
B963A5CC2853870000EFADA0 /* OrderItemRefundMetaData.swift in Sources */,
31623165
E18152BE28F85B5B0011A0EC /* InAppPurchasesRemote.swift in Sources */,
31633166
0329CF9B27A82E19008AFF91 /* WCPayCharge.swift in Sources */,
3167+
035BA3AA29113CBD0056F0AD /* DataBoolMapper.swift in Sources */,
31643168
74749B97224134FF005C4CF2 /* ProductMapper.swift in Sources */,
31653169
26455E2A25F669F0008A1D32 /* ProductAttributeTermMapper.swift in Sources */,
31663170
0359EA1127AAC6740048DE2D /* WCPayPaymentMethodType.swift in Sources */,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import Foundation
2+
3+
/// Mapper: Bool Result Wrapped in `data` Key
4+
///
5+
struct DataBoolMapper: Mapper {
6+
7+
/// (Attempts) to extract the boolean flag from a given JSON Encoded response.
8+
///
9+
func map(response: Data) throws -> Bool {
10+
try JSONDecoder().decode(DataBool.self, from: response).data
11+
}
12+
}
13+
14+
/// DataBoolResultEnvelope Disposable Entity
15+
///
16+
/// Some endpoints return a Bool response in the `data` key. This entity
17+
/// allows us to parse that response with JSONDecoder.
18+
///
19+
private struct DataBool: Decodable {
20+
let data: Bool
21+
22+
private enum CodingKeys: String, CodingKey {
23+
case data
24+
}
25+
}

Networking/Networking/Remote/JustInTimeMessagesRemote.swift

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import Foundation
33
public protocol JustInTimeMessagesRemoteProtocol {
44
func loadAllJustInTimeMessages(for siteID: Int64,
55
messagePath: JustInTimeMessagesRemote.MessagePath) async -> Result<[JustInTimeMessage], Error>
6+
func dismissJustInTimeMessage(for siteID: Int64,
7+
messageID: String,
8+
featureClass: String) async -> Result<Bool, Error>
69
}
710

811
/// Just In Time Messages: Remote endpoints
@@ -15,7 +18,8 @@ public final class JustInTimeMessagesRemote: Remote, JustInTimeMessagesRemotePro
1518
/// - Parameters:
1619
/// - siteID: The site for which we'll fetch JustInTimeMessages.
1720
/// - messagePath: The location for JITMs to be displayed
18-
/// - completion: Closure to be executed upon completion.
21+
/// - Returns:
22+
/// Async result with an array of `[JustInTimeMessage]` (usually contains one element) or an error
1923
///
2024
public func loadAllJustInTimeMessages(for siteID: Int64,
2125
messagePath: JustInTimeMessagesRemote.MessagePath) async -> Result<[JustInTimeMessage], Error> {
@@ -34,6 +38,36 @@ public final class JustInTimeMessagesRemote: Remote, JustInTimeMessagesRemotePro
3438
return .failure(error)
3539
}
3640
}
41+
42+
/// Dismisses a `JustInTimeMessage` using the API.
43+
///
44+
/// - Parameters:
45+
/// - siteID: The site for which we'll dismiss a JustInTimeMessage
46+
/// - messageID: The ID of the JustInTimeMessage that was dismissed
47+
/// - featureClass: The featureClass of the JustInTimeMessages that should be dismissed
48+
/// - Returns:
49+
/// Async result with a `Bool` indicating whether dismissal was successful, or an error
50+
///
51+
public func dismissJustInTimeMessage(for siteID: Int64,
52+
messageID: String,
53+
featureClass: String) async -> Result<Bool, Error> {
54+
55+
let parameters = [ParameterKey.featureClass: featureClass,
56+
ParameterKey.messageID: messageID]
57+
58+
let request = JetpackRequest(wooApiVersion: .none,
59+
method: .post,
60+
siteID: siteID,
61+
path: Path.jitm,
62+
parameters: parameters)
63+
64+
do {
65+
let result = try await enqueue(request, mapper: DataBoolMapper())
66+
return result
67+
} catch {
68+
return .failure(error)
69+
}
70+
}
3771
}
3872

3973
// MARK: - Constants
@@ -45,6 +79,8 @@ public extension JustInTimeMessagesRemote {
4579

4680
private enum ParameterKey {
4781
static let messagePath = "message_path"
82+
static let featureClass = "feature_class"
83+
static let messageID = "id"
4884
}
4985

5086
/// Message Path parameter

WooCommerce/Classes/Analytics/WooAnalyticsEvent.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,40 @@ extension WooAnalyticsEvent {
639639
Keys.justInTimeMessageGroup: featureClass
640640
])
641641
}
642+
643+
static func dismissTapped(source: String,
644+
messageID: String,
645+
featureClass: String) -> WooAnalyticsEvent {
646+
WooAnalyticsEvent(statName: .justInTimeMessageDismissTapped,
647+
properties: [
648+
Keys.source: source,
649+
Keys.justInTimeMessageID: messageID,
650+
Keys.justInTimeMessageGroup: featureClass
651+
])
652+
}
653+
654+
static func dismissSuccess(source: String,
655+
messageID: String,
656+
featureClass: String) -> WooAnalyticsEvent {
657+
WooAnalyticsEvent(statName: .justInTimeMessageDismissSuccess, properties: [
658+
Keys.source: source,
659+
Keys.justInTimeMessageID: messageID,
660+
Keys.justInTimeMessageGroup: featureClass
661+
])
662+
}
663+
664+
static func dismissFailure(source: String,
665+
messageID: String,
666+
featureClass: String,
667+
error: Error) -> WooAnalyticsEvent {
668+
WooAnalyticsEvent(statName: .justInTimeMessageDismissFailure,
669+
properties: [
670+
Keys.source: source,
671+
Keys.justInTimeMessageID: messageID,
672+
Keys.justInTimeMessageGroup: featureClass
673+
],
674+
error: error)
675+
}
642676
}
643677
}
644678

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,9 @@ public enum WooAnalyticsStat: String {
629629

630630
// MARK: Just In Time Messages events
631631
case justInTimeMessageCallToActionTapped = "jitm_cta_tapped"
632+
case justInTimeMessageDismissTapped = "jitm_dismissed"
633+
case justInTimeMessageDismissSuccess = "jitm_dismiss_success"
634+
case justInTimeMessageDismissFailure = "jitm_dismiss_failure"
632635

633636
// MARK: Simple Payments events
634637
//

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

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ final class JustInTimeMessageAnnouncementCardViewModel: AnnouncementCardViewMode
99

1010
private let analytics: Analytics
1111

12+
private let stores: StoresManager
13+
14+
private let justInTimeMessage: YosemiteJustInTimeMessage
15+
1216
// MARK: - Message properties
1317
let title: String
1418

@@ -27,9 +31,11 @@ final class JustInTimeMessageAnnouncementCardViewModel: AnnouncementCardViewMode
2731
init(justInTimeMessage: YosemiteJustInTimeMessage,
2832
screenName: String,
2933
siteID: Int64,
34+
stores: StoresManager = ServiceLocator.stores,
3035
analytics: Analytics = ServiceLocator.analytics) {
3136
self.siteID = siteID
3237
self.analytics = analytics
38+
self.stores = stores
3339
let utmProvider = WooCommerceComUTMProvider(
3440
campaign: "jitm_group_\(justInTimeMessage.featureClass)",
3541
source: screenName,
@@ -38,6 +44,7 @@ final class JustInTimeMessageAnnouncementCardViewModel: AnnouncementCardViewMode
3844
self.url = utmProvider.urlWithUtmParams(string: justInTimeMessage.url)
3945
self.messageID = justInTimeMessage.messageID
4046
self.featureClass = justInTimeMessage.featureClass
47+
self.justInTimeMessage = justInTimeMessage
4148
self.screenName = screenName
4249
self.title = justInTimeMessage.title
4350
self.message = justInTimeMessage.detail
@@ -68,10 +75,9 @@ final class JustInTimeMessageAnnouncementCardViewModel: AnnouncementCardViewMode
6875
}
6976

7077
func ctaTapped() {
71-
analytics.track(event: WooAnalyticsEvent.JustInTimeMessage.callToActionTapped(
72-
source: screenName,
73-
messageID: messageID,
74-
featureClass: featureClass))
78+
analytics.track(event: .JustInTimeMessage.callToActionTapped(source: screenName,
79+
messageID: messageID,
80+
featureClass: featureClass))
7581

7682
guard let url = url else {
7783
return
@@ -91,7 +97,30 @@ final class JustInTimeMessageAnnouncementCardViewModel: AnnouncementCardViewMode
9197
}
9298

9399
func dontShowAgainTapped() {
94-
// No-op
100+
analytics.track(event: .JustInTimeMessage.dismissTapped(source: screenName,
101+
messageID: messageID,
102+
featureClass: featureClass))
103+
let action = JustInTimeMessageAction.dismissMessage(justInTimeMessage,
104+
siteID: siteID,
105+
completion: { result in
106+
// We deliberately strongly capture self here: the owning reference to the VM may have been
107+
// set to nil by now, in order to stop displaying the Just In Time Message.
108+
// [weak self] will result in these two analytics never being logged.
109+
switch result {
110+
case .success(_):
111+
self.analytics.track(event: .JustInTimeMessage.dismissSuccess(
112+
source: self.screenName,
113+
messageID: self.messageID,
114+
featureClass: self.featureClass))
115+
case .failure(let error):
116+
self.analytics.track(event: .JustInTimeMessage.dismissFailure(
117+
source: self.screenName,
118+
messageID: self.messageID,
119+
featureClass: self.featureClass,
120+
error: error))
121+
}
122+
})
123+
stores.dispatch(action)
95124
}
96125

97126
func remindLaterTapped() {

WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,12 @@ private extension DashboardViewController {
317317
return
318318
}
319319

320-
let cardView = FeatureAnnouncementCardView(viewModel: viewModel,
321-
dismiss: {},
322-
callToAction: {})
320+
let cardView = FeatureAnnouncementCardView(
321+
viewModel: viewModel,
322+
dismiss: { [weak self] in
323+
self?.viewModel.announcementViewModel = nil
324+
},
325+
callToAction: {})
323326

324327
self.showAnnouncement(AnnouncementCardWrapper(cardView: cardView))
325328
}

WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ final class DashboardViewModel {
88
/// Stats v4 is shown by default, then falls back to v3 if store stats are unavailable.
99
@Published private(set) var statsVersion: StatsVersion = .v4
1010

11-
@Published private(set) var announcementViewModel: AnnouncementCardViewModelProtocol? = nil
11+
@Published var announcementViewModel: AnnouncementCardViewModelProtocol? = nil
1212

1313
@Published private(set) var showWebViewSheet: WebViewSheetViewModel? = nil
1414

0 commit comments

Comments
 (0)